|
From: Pekka Helenius <fincer89@hotmail.com>
|
|
Date: Tue, 04 Aug 2020 01:52:18 +0300
|
|
Subject: Add user agent string support for HTTPS constraints, update ChangeLog
|
|
|
|
|
|
--- a/src/parse.y 2020-08-03 23:22:43.401482642 +0300
|
|
+++ b/src/parse.y 2020-08-04 01:03:44.458183310 +0300
|
|
@@ -69,6 +69,7 @@ struct opts {
|
|
int trusted;
|
|
char *refstr;
|
|
int port;
|
|
+ char *useragent;
|
|
int pos_num;
|
|
double pos_decimal;
|
|
} opts;
|
|
@@ -91,6 +92,7 @@ typedef struct {
|
|
%token SERVER SERVERS SENSOR CORRECTION RTABLE REFID STRATUM WEIGHT
|
|
%token ERROR
|
|
%token PORT
|
|
+%token USERAGENT
|
|
|
|
%token _NTPD_USER
|
|
%token _DRIFTFILE
|
|
@@ -164,6 +166,7 @@ typedef struct {
|
|
%type <v.opts> weight
|
|
%type <v.opts> trusted
|
|
%type <v.opts> port
|
|
+%type <v.opts> useragent
|
|
|
|
%type <v.opts> pos_num
|
|
%type <v.opts> pos_decimal
|
|
@@ -376,6 +379,8 @@ main : LISTEN ON address listen_opts {
|
|
|
|
p->addr_head.pool = ++poolseqnum;
|
|
p->addr_head.name = strdup($3->name);
|
|
+ if ($4.useragent)
|
|
+ p->useragent = strdup($4.useragent);
|
|
p->addr_head.path = strdup($3->path);
|
|
if (p->addr_head.name == NULL ||
|
|
p->addr_head.path == NULL)
|
|
@@ -425,6 +430,8 @@ main : LISTEN ON address listen_opts {
|
|
|
|
p->addr_head.pool = 0;
|
|
p->addr_head.name = strdup($3->name);
|
|
+ if ($4.useragent)
|
|
+ p->useragent = strdup($4.useragent);
|
|
p->addr_head.path = strdup($3->path);
|
|
if (p->addr_head.name == NULL ||
|
|
p->addr_head.path == NULL)
|
|
@@ -735,7 +742,7 @@ constraint_opts : { opts_default(); }
|
|
constraint_opts_l : constraint_opts_l constraint_opt
|
|
| constraint_opt
|
|
;
|
|
-constraint_opt : port
|
|
+constraint_opt : port | useragent
|
|
;
|
|
|
|
sensor_opts : { opts_default(); }
|
|
@@ -803,6 +810,19 @@ port : PORT NUMBER {
|
|
}
|
|
;
|
|
|
|
+useragent : USERAGENT STRING {
|
|
+ size_t l = strlen($2);
|
|
+
|
|
+ if (l < 1 || l > USERAGENT_MAX_LENGTH) {
|
|
+ yyerror("user agent string length must be from 1 to %d characters",
|
|
+ USERAGENT_MAX_LENGTH);
|
|
+ free($2);
|
|
+ YYERROR;
|
|
+ }
|
|
+ opts.useragent = $2;
|
|
+ }
|
|
+ ;
|
|
+
|
|
rtable : RTABLE NUMBER {
|
|
#ifdef RT_TABLEID_MAX
|
|
if ($2 < 0 || $2 > RT_TABLEID_MAX) {
|
|
@@ -924,6 +944,7 @@ lookup(char *s)
|
|
{ "trustlevel_badpeer", _TRUSTLEVEL_BADPEER, "single" },
|
|
{ "trustlevel_max", _TRUSTLEVEL_MAX, "single" },
|
|
{ "trustlevel_pathetic", _TRUSTLEVEL_PATHETIC, "single" },
|
|
+ { "useragent", USERAGENT, "multiple" },
|
|
{ "weight", WEIGHT, "multiple" },
|
|
};
|
|
struct keywords *p;
|
|
--- a/src/ntpd.h 2020-08-03 23:25:02.978705101 +0300
|
|
+++ b/src/ntpd.h 2020-08-03 23:41:03.644863921 +0300
|
|
@@ -114,6 +114,8 @@
|
|
#define CONSTRAINT_CA SYSCONFDIR "/ssl/cert.pem"
|
|
#define CONSTRAINT_CA_VALIDATION 0x01;
|
|
|
|
+#define USERAGENT_MAX_LENGTH 256
|
|
+
|
|
#define PARENT_SOCK_FILENO CONSTRAINT_PASSFD
|
|
|
|
#define NTP_PROC_NAME "ntp_main"
|
|
@@ -159,9 +161,11 @@ struct ntp_addr_wrap {
|
|
struct ntp_addr_msg {
|
|
struct ntp_addr a;
|
|
int port;
|
|
- size_t namelen;
|
|
- size_t pathlen;
|
|
+ char useragent[USERAGENT_MAX_LENGTH];
|
|
+ size_t namelen;
|
|
+ size_t pathlen;
|
|
size_t portlen;
|
|
+ size_t agentlen;
|
|
};
|
|
|
|
struct ntp_status {
|
|
@@ -239,6 +243,7 @@ struct constraint {
|
|
time_t last;
|
|
time_t constraint;
|
|
int dnstries;
|
|
+ char *useragent;
|
|
};
|
|
|
|
struct ntp_conf_sensor {
|
|
@@ -495,6 +500,7 @@ struct httpsdate {
|
|
char *tls_hostname;
|
|
char *tls_path;
|
|
char *tls_request;
|
|
+ char *tls_useragent;
|
|
struct tls_config *tls_config;
|
|
struct tls *tls_ctx;
|
|
struct tm tls_tm;
|
|
@@ -502,11 +508,11 @@ struct httpsdate {
|
|
|
|
struct httpsdate *
|
|
httpsdate_init(const char *, const int *, const char *,
|
|
- const char *, const u_int8_t *, size_t);
|
|
+ const char *, const char *, const u_int8_t *, size_t);
|
|
void httpsdate_free(void *);
|
|
int httpsdate_request(struct httpsdate *, struct timeval *);
|
|
void *httpsdate_query(const char *, const int *, const char *,
|
|
- const char *, const u_int8_t *, size_t,
|
|
+ const char *, const char *, const u_int8_t *, size_t,
|
|
struct timeval *, struct timeval *);
|
|
|
|
char *tls_readline(struct tls *, size_t *, size_t *, struct timeval *);
|
|
--- a/src/constraint.c 2020-08-02 01:58:28.366952848 +0300
|
|
+++ b/src/constraint.c 2020-08-03 23:43:48.584926017 +0300
|
|
@@ -132,7 +132,7 @@ constraint_query(struct constraint *cstr
|
|
{
|
|
time_t now;
|
|
struct ntp_addr_msg am;
|
|
- struct iovec iov[4];
|
|
+ struct iovec iov[5];
|
|
int iov_cnt = 0;
|
|
|
|
now = getmonotime();
|
|
@@ -185,6 +185,18 @@ constraint_query(struct constraint *cstr
|
|
memcpy(&am.a, cstr->addr, sizeof(am.a));
|
|
memcpy(&am.port, &cstr->addr_head.port, sizeof(am.port));
|
|
|
|
+ if (cstr->useragent)
|
|
+ memcpy(&am.useragent, cstr->useragent, USERAGENT_MAX_LENGTH);
|
|
+/*
|
|
+ * FIXME For some reason, dynamically allocating user agent string size does not seem to work
|
|
+ * Investigate, why usage of dynamic memory allocated data fails.
|
|
+ *
|
|
+ if (cstr->useragent) {
|
|
+ if ((am.useragent = calloc(1, strlen(cstr->useragent)) ) == NULL)
|
|
+ fatal("constraint id %d: can't allocate memory for user agent string", cstr->id);
|
|
+ memcpy(am.useragent, cstr->useragent, strlen(cstr->useragent));
|
|
+ }
|
|
+*/
|
|
iov[iov_cnt].iov_base = &am;
|
|
iov[iov_cnt++].iov_len = sizeof(am);
|
|
if (cstr->addr_head.name) {
|
|
@@ -202,6 +214,11 @@ constraint_query(struct constraint *cstr
|
|
iov[iov_cnt].iov_base = &cstr->addr_head.port;
|
|
iov[iov_cnt++].iov_len = am.portlen;
|
|
}
|
|
+ if (cstr->useragent) {
|
|
+ am.agentlen = strlen(cstr->useragent) + 1;
|
|
+ iov[iov_cnt].iov_base = cstr->useragent;
|
|
+ iov[iov_cnt++].iov_len = am.agentlen;
|
|
+ }
|
|
|
|
imsg_composev(ibuf_main, IMSG_CONSTRAINT_QUERY,
|
|
cstr->id, 0, -1, iov, iov_cnt);
|
|
@@ -228,8 +245,9 @@ priv_constraint_msg(u_int32_t id, u_int8
|
|
log_warnx("constraint id %d: longer query expected", id);
|
|
return;
|
|
}
|
|
+
|
|
memcpy(&am, data, sizeof(am));
|
|
- if (len != (sizeof(am) + am.namelen + am.pathlen + am.portlen)) {
|
|
+ if (len != (sizeof(am) + am.namelen + am.pathlen + am.portlen + am.agentlen)) {
|
|
log_warnx("constraint id %d: invalid query received", id);
|
|
return;
|
|
}
|
|
@@ -238,6 +256,7 @@ priv_constraint_msg(u_int32_t id, u_int8
|
|
if ((h = calloc(1, sizeof(*h))) == NULL)
|
|
fatal("constraint id %d: can't allocate memory for network address", id);
|
|
memcpy(h, &am.a, sizeof(*h));
|
|
+
|
|
h->next = NULL;
|
|
|
|
cstr = new_constraint();
|
|
@@ -308,7 +327,7 @@ priv_constraint_readquery(struct constra
|
|
);
|
|
|
|
memcpy(am, imsg.data, sizeof(*am));
|
|
- if (mlen != (sizeof(*am) + am->namelen + am->pathlen + am->portlen))
|
|
+ if (mlen != (sizeof(*am) + am->namelen + am->pathlen + am->portlen + am->agentlen))
|
|
fatalx("constraint: invalid message length received from parent process (%s)",
|
|
__func__
|
|
);
|
|
@@ -327,6 +346,8 @@ priv_constraint_readquery(struct constra
|
|
cstr->port = port;
|
|
cstr->addr_head.port = port;
|
|
|
|
+ cstr->useragent = strdup(am->useragent);
|
|
+
|
|
dptr = imsg.data;
|
|
memcpy(*data, dptr + sizeof(*am), mlen - sizeof(*am));
|
|
imsg_free(&imsg);
|
|
@@ -464,7 +485,7 @@ priv_constraint_child(const char *pw_dir
|
|
log_debug("constraint %s: initializing HTTPS request", addr);
|
|
if ((ctx = httpsdate_query(addr,
|
|
&cstr.addr_head.port, cstr.addr_head.name, cstr.addr_head.path,
|
|
- conf->ca, conf->ca_len, &rectv, &xmttv)) == NULL) {
|
|
+ cstr.useragent, conf->ca, conf->ca_len, &rectv, &xmttv)) == NULL) {
|
|
log_debug("constraint %s: failed to get proper time results", addr);
|
|
/* Abort with failure but without warning */
|
|
exit(1);
|
|
@@ -852,6 +873,9 @@ constraint_msg_dns(u_int32_t id, u_int8_
|
|
ncstr->addr_head.name = strdup(cstr->addr_head.name);
|
|
ncstr->addr_head.path = strdup(cstr->addr_head.path);
|
|
ncstr->addr_head.port = intdup(cstr->addr_head.port);
|
|
+
|
|
+ if (cstr->useragent)
|
|
+ ncstr->useragent = strdup(cstr->useragent);
|
|
|
|
// Unless we do this, we have value 0 in ncstr->port
|
|
ncstr->port = intdup(cstr->port);
|
|
--- a/src/constraint-libressl.c 2020-08-01 19:50:24.130263065 +0300
|
|
+++ b/src/constraint-libressl.c 2020-08-04 00:01:30.008179382 +0300
|
|
@@ -26,7 +26,7 @@
|
|
|
|
struct httpsdate *
|
|
httpsdate_init(const char *addr, const int *port, const char *hostname,
|
|
- const char *path, const u_int8_t *ca, size_t ca_len)
|
|
+ const char *path, const char *useragent, const u_int8_t *ca, size_t ca_len)
|
|
{
|
|
struct httpsdate *httpsdate = NULL;
|
|
char port_s[sizeof(port)];
|
|
@@ -45,10 +45,23 @@ httpsdate_init(const char *addr, const i
|
|
(httpsdate->tls_path = strdup(path)) == NULL)
|
|
goto fail;
|
|
|
|
- if (asprintf(&httpsdate->tls_request,
|
|
- "HEAD %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n",
|
|
- httpsdate->tls_path, httpsdate->tls_hostname) == -1)
|
|
- goto fail;
|
|
+ if (useragent)
|
|
+ httpsdate->tls_useragent = strdup(useragent);
|
|
+
|
|
+ if (httpsdate->tls_useragent) {
|
|
+ if (asprintf(&httpsdate->tls_request,
|
|
+ "HEAD %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: %s\r\nConnection: close\r\n\r\n",
|
|
+ httpsdate->tls_path,
|
|
+ httpsdate->tls_hostname,
|
|
+ httpsdate->tls_useragent) == -1)
|
|
+ goto fail;
|
|
+ } else {
|
|
+ if (asprintf(&httpsdate->tls_request,
|
|
+ "HEAD %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n",
|
|
+ httpsdate->tls_path,
|
|
+ httpsdate->tls_hostname) == -1)
|
|
+ goto fail;
|
|
+ }
|
|
|
|
if ((httpsdate->tls_config = tls_config_new()) == NULL)
|
|
goto fail;
|
|
@@ -110,6 +123,10 @@ httpsdate_request(struct httpsdate *http
|
|
if (tls_configure(httpsdate->tls_ctx, httpsdate->tls_config) == -1)
|
|
goto fail;
|
|
|
|
+ if (httpsdate->tls_useragent)
|
|
+ log_debug("constraint %s: user agent: %s", httpsdate->tls_addr,
|
|
+ httpsdate->tls_useragent);
|
|
+
|
|
/*
|
|
* LibreSSL expects an address string, which can also be a DNS name,
|
|
* but we pass a pre-resolved IP address string in tls_addr so it
|
|
@@ -222,14 +239,14 @@ httpsdate_request(struct httpsdate *http
|
|
|
|
void *
|
|
httpsdate_query(const char *addr, const int *port, const char *hostname,
|
|
- const char *path, const u_int8_t *ca, size_t ca_len,
|
|
+ const char *path, const char *useragent, const u_int8_t *ca, size_t ca_len,
|
|
struct timeval *rectv, struct timeval *xmttv)
|
|
{
|
|
struct httpsdate *httpsdate;
|
|
struct timeval when;
|
|
time_t t;
|
|
|
|
- if ((httpsdate = httpsdate_init(addr, port, hostname, path,
|
|
+ if ((httpsdate = httpsdate_init(addr, port, hostname, path, useragent,
|
|
ca, ca_len)) == NULL)
|
|
return (NULL);
|
|
|
|
--- a/src/constraint-openssl.c 2020-08-03 19:23:54.377109002 +0300
|
|
+++ b/src/constraint-openssl.c 2020-08-04 00:01:16.178179367 +0300
|
|
@@ -37,11 +37,20 @@ o_httpsdate_init(struct constraint *cstr
|
|
if ((httpsdate->cstr = cstr) == NULL)
|
|
goto fail;
|
|
|
|
- if (asprintf(&httpsdate->tls_request,
|
|
- "HEAD %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n",
|
|
- httpsdate->cstr->addr_head.path,
|
|
- httpsdate->cstr->addr_head.name) == -1)
|
|
- goto fail;
|
|
+ if (httpsdate->cstr->useragent) {
|
|
+ if (asprintf(&httpsdate->tls_request,
|
|
+ "HEAD %s HTTP/1.1\r\nHost: %s\r\nUser-Agent: %s\r\nConnection: close\r\n\r\n",
|
|
+ httpsdate->cstr->addr_head.path,
|
|
+ httpsdate->cstr->addr_head.name,
|
|
+ httpsdate->cstr->useragent) == -1)
|
|
+ goto fail;
|
|
+ } else {
|
|
+ if (asprintf(&httpsdate->tls_request,
|
|
+ "HEAD %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n",
|
|
+ httpsdate->cstr->addr_head.path,
|
|
+ httpsdate->cstr->addr_head.name) == -1)
|
|
+ goto fail;
|
|
+ }
|
|
|
|
if ((httpsdate->tls_method = TLS_method()) == NULL)
|
|
goto fail;
|
|
@@ -161,6 +170,10 @@ o_httpsdate_request(struct o_httpsdate *
|
|
goto fail;
|
|
}
|
|
|
|
+ if (httpsdate->cstr->useragent)
|
|
+ log_debug("constraint %s: user agent: %s", ia_str,
|
|
+ httpsdate->cstr->useragent);
|
|
+
|
|
log_debug("constraint %s: establishing connection", ia_str);
|
|
ret = SSL_connect(httpsdate->tls_conn);
|
|
if (ret < 1) {
|
|
--- a/src/ntpd.conf.5 2020-08-03 23:21:11.124672226 +0300
|
|
+++ b/src/ntpd.conf.5 2020-08-04 01:38:53.878185531 +0300
|
|
@@ -14,7 +14,7 @@
|
|
.\" AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
|
|
.\" OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
.\"
|
|
-.Dd $Mdocdate: August 01 2020 $
|
|
+.Dd $Mdocdate: August 04 2020 $
|
|
.Dt NTPD.CONF 5
|
|
.Os
|
|
.Sh NAME
|
|
@@ -58,14 +58,12 @@ is given as an address,
|
|
.Xr ntpd 8
|
|
will listen on all local addresses using the specified routing table.
|
|
.Xr ntpd 8
|
|
-does not listen on any address by default.
|
|
-The optional
|
|
+does not listen on any address by default. The optional
|
|
.Ic rtable
|
|
keyword will specify which routing table to listen on, if the operating system supports rdomains.
|
|
By default
|
|
.Xr ntpd 8
|
|
-will listen using the current routing table.
|
|
-The optional
|
|
+will listen using the current routing table. The optional
|
|
.Ic port
|
|
keyword will specify which local UDP port the NTP server process should use for inbound connections.
|
|
By default
|
|
@@ -240,18 +238,29 @@ Configuring a constraint without proper
|
|
.Xr ntpd 8
|
|
to log a warning message on startup.
|
|
.Bl -tag -width Ds
|
|
-.It Ic constraint from Ar url [ip...]
|
|
+.It Xo Ic constraint from Ar url
|
|
+.Op Ar ip...
|
|
.Op Ic port Ar port-number
|
|
+.Op Ic useragent Ar agent-string
|
|
+.Xc
|
|
Specify the URL, IP address or the hostname of an HTTPS server to
|
|
provide a constraint. The optional
|
|
.Ic port
|
|
number is an HTTPS server port to connect to. By default
|
|
.Xr ntpd 8
|
|
-will connect to remote TCP port 443.
|
|
-If the url is followed by one or more addresses the url and addresses will be
|
|
+will connect to remote TCP port 443. The optional
|
|
+.Ic useragent
|
|
+string adds a defined user agent string into a constraint HTTP
|
|
+request. This option helps retrieving a time constraint from HTTPS
|
|
+servers which block user agentless HTTP
|
|
+requests. If the
|
|
+.Ic url
|
|
+is followed by one or more
|
|
+.Ic ip
|
|
+addresses the URL and addresses will be
|
|
tried until a working one is found.
|
|
-The url path and expected certificate name is always taken from the
|
|
-url specified.
|
|
+The URL path and expected certificate name is always taken from the
|
|
+URL specified.
|
|
If
|
|
.Ic constraint from
|
|
is used more than once,
|
|
@@ -259,29 +268,34 @@ is used more than once,
|
|
will calculate a median constraint from all the servers specified.
|
|
.Bd -literal -offset indent
|
|
server ntp.example.org
|
|
-constraint from www.example.com
|
|
+constraint from "www.example.com"
|
|
constraint from "https://9.9.9.9" "2620:fe::9"
|
|
-constraint from www.google.com port 443
|
|
+constraint from "www.google.com" port 443 useragent "OpenNTPD time query"
|
|
.Ed
|
|
-.It Ic constraints from Ar url
|
|
+.It Xo Ic constraints from Ar url
|
|
.Op Ic port Ar port-number
|
|
+.Op Ic useragent Ar agent-string
|
|
+.Xc
|
|
As with
|
|
.Ic constraint from ,
|
|
specify the URL, IP address or the hostname of an HTTPS server to
|
|
provide a constraint.
|
|
Should the hostname resolve to multiple IP addresses,
|
|
.Xr ntpd 8
|
|
-will calculate a median constraint from all of them.
|
|
-The optional
|
|
+will calculate a median constraint from all of them. The optional
|
|
.Ic port
|
|
number is an HTTPS server port to connect to. By default
|
|
.Xr ntpd 8
|
|
-will connect to remote TCP port 443.
|
|
+will connect to remote TCP port 443. The optional
|
|
+.Ic useragent
|
|
+string adds a defined user agent string into a constraint HTTP
|
|
+request. This option helps retrieving a time constraint from HTTPS
|
|
+servers which block user agentless HTTP requests.
|
|
For example:
|
|
.Bd -literal -offset indent
|
|
servers pool.ntp.org
|
|
constraints from "https://www.google.com/"
|
|
-constraints from "https://duckduckgo.com/" port 443
|
|
+constraints from "https://duckduckgo.com/" port 443 useragent "OpenNTPD time query"
|
|
.Ed
|
|
.El
|
|
.Sh ADVANCED KEYWORDS
|
|
@@ -676,6 +690,16 @@ Negligible drift time to not log in mill
|
|
32
|
|
.El
|
|
.Ed
|
|
+.It Ic max_display_width Ar number
|
|
+Maximum number of characters in a
|
|
+.Xr ntpctl 8
|
|
+report line (peers, status, sensors and all).
|
|
+.Bd -literal -offset indent
|
|
+.Bl -tag -width "Default:" -compact
|
|
+.It Default:
|
|
+80
|
|
+.El
|
|
+.Ed
|
|
.It Ic max_frequency_adjust Ar decimal
|
|
Maximum allowed frequency correction per iteration.
|
|
.Bd -literal -offset indent
|
|
@@ -780,6 +804,14 @@ Sensor default reference ID string.
|
|
"HARD"
|
|
.El
|
|
.Ed
|
|
+.It Ic sensor_offsets Ar seconds
|
|
+Maximum allowed sensor time offset in seconds.
|
|
+.Bd -literal -offset indent
|
|
+.Bl -tag -width "Default:" -compact
|
|
+.It Default:
|
|
+6
|
|
+.El
|
|
+.Ed
|
|
.It Ic sensor_query_interval Ar seconds
|
|
Sensor query interval in seconds.
|
|
.Bd -literal -offset indent
|
|
--- a/ChangeLog 2020-08-02 14:01:47.093664999 +0300
|
|
+++ b/ChangeLog 2020-08-04 01:52:06.054853035 +0300
|
|
@@ -32,6 +32,10 @@ OpenNTPD Portable Release Notes:
|
|
|
|
* Many previously hardcoded values are now configurable via conf file.
|
|
|
|
+ * Implement UDP & TCP port selection for multiple options.
|
|
+
|
|
+ * Implement custom user agent string support for constraints.
|
|
+
|
|
* Implemented OpenSSL support. Either LibreSSL or OpenSSL can be used.
|
|
|
|
* Improved log entries interpretation.
|