|
|
- From: Pekka Helenius <fincer89@hotmail.com>
- Date: Tue, 04 Aug 2020 01:52:16 +0300
- Subject: Implement OpenSSL support, update manual, update ChangeLog
-
-
- --- a/configure.ac 2020-07-31 23:00:40.000000000 +0300
- +++ b/configure.ac 2020-08-02 01:23:30.696950640 +0300
- @@ -59,35 +59,100 @@ AM_CONDITIONAL([HAVE_ADJFREQ], [test "x$
- AM_CONDITIONAL([HAVE_CLOCK_GETRES], [test "x$ac_cv_func_clock_getres" = xyes])
- AM_CONDITIONAL([HAVE_CLOCK_GETTIME], [test "x$ac_cv_func_clock_gettime" = xyes])
-
- -# check for libtls
- -AC_SEARCH_LIBS([tls_config_set_ca_mem],[tls],
- - [LIBS="$LIBS -ltls -lssl -lcrypto"],,[-lssl -lcrypto])
- -AC_CHECK_FUNCS([tls_config_set_ca_mem])
- -
- -# check if libtls uses 3-argument tls_write
- -AC_CACHE_CHECK([if tls_write takes 3 arguments], ac_cv_have_tls_write_3_arg, [
- - AC_LINK_IFELSE([AC_LANG_PROGRAM([[
- -#include <tls.h>
- -size_t outlen;
- - ]], [[ tls_write(NULL, NULL, 0); ]])],
- - [ ac_cv_have_tls_write_3_arg="yes" ],
- - [ ac_cv_have_tls_write_3_arg="no"
- +# NOTE: hard-set AC_CHECK_HEADER or friends can't really be checked since
- +# libressl/openssl include header files must be determined by OS basis
- +# during compilation. Use the following approach instead.
- +
- +AM_CONDITIONAL(HAVE_SSL, false)
- +AM_CONDITIONAL([HAVE_LIBRESSL], false)
- +AM_CONDITIONAL([HAVE_OPENSSL], false)
- +
- +# check for libressl
- +AC_ARG_WITH([libressl],
- + AS_HELP_STRING([--without-libressl],
- + [Disable LibreSSL support for constraints]))
- +
- +AS_IF([test "x$with_libressl" != "xno" ],
- + [ have_libressl="yes" ],
- + [ have_libressl="no" ]
- +)
- +
- +if test "x$have_libressl" == "xyes"; then
- +
- + AC_SEARCH_LIBS([tls_config_set_ca_mem],[tls],
- + [LIBS="$LIBS -ltls -lssl -lcrypto"],,[-lssl -lcrypto])
- +
- + AC_CHECK_FUNCS([tls_config_set_ca_mem])
- +
- + # check if libressl uses 3-argument tls_write
- + AC_CACHE_CHECK(
- + [if LibreSSL tls_write takes 3 arguments],
- + ac_cv_have_libressl_write_3_arg,
- + [AC_LINK_IFELSE([AC_LANG_PROGRAM(
- + [[ #include <libressl/tls.h> ]],
- + [[ size_t outlen; ]],
- + [[ tls_write(NULL, NULL, 0); ]]
- + )],
- + [ ac_cv_have_libressl_write_3_arg="yes" ],
- + [ ac_cv_have_libressl_write_3_arg="no" ]
- + )
- + ])
- +fi
- +
- +# check for openssl
- +AC_ARG_WITH([openssl],
- + AS_HELP_STRING([--without-openssl],
- + [Disable OpenSSL support for constraints]))
- +
- +AS_IF([test "x$with_openssl" != "xno" ],
- + [ have_openssl="yes" ],
- + [ have_openssl="no" ]
- +)
- +
- +if test "x$have_openssl" == "xyes"; then
- +
- + AC_SEARCH_LIBS([X509_STORE_load_locations],[ssl],
- + [LIBS="$LIBS -lssl -lcrypto"],,[-lssl -lcrypto])
- +
- + AC_CHECK_FUNCS([X509_STORE_load_locations])
- +
- + # check if openssl uses 3-argument SSL_write
- + AC_CACHE_CHECK(
- + [if OpenSSL SSL_write takes 3 arguments],
- + ac_cv_have_openssl_write_3_arg,
- + [AC_LINK_IFELSE([AC_LANG_PROGRAM(
- + [[ #include <openssl/ssl.h> ]],
- + [[ SSL *a; SSL_CTX *ff; ]],
- + [[ ff = SSL_CTX_new(TLS_method()); ]],
- + [[ a = SSL_new(ff); ]],
- + [[ SSL_write(a, NULL, 0); ]]
- + )],
- + [ ac_cv_have_openssl_write_3_arg="yes" ],
- + [ ac_cv_have_openssl_write_3_arg="no" ]
- + )
- ])
- -])
- +fi
-
- -AC_ARG_ENABLE([https-constraint],
- - AS_HELP_STRING([--disable-https-constraint],
- - [Disable HTTPS Constraint Functionality]))
- -
- -AM_CONDITIONAL([HAVE_LIBTLS],
- - [test "x$ac_cv_func_tls_config_set_ca_mem" = xyes \
- - -a "x$ac_cv_have_tls_write_3_arg" = xyes \
- - -a "x$enable_https_constraint" != xno])
- -
- -if test "x$ac_cv_func_tls_config_set_ca_mem" = xyes \
- - -a "x$ac_cv_have_tls_write_3_arg" = xyes \
- - -a "x$enable_https_constraint" != xno; then
- - AC_DEFINE([HAVE_LIBTLS], [yes])
- +if test "x$with_libressl" != xno \
- + -a "x$ac_cv_func_tls_config_set_ca_mem" = xyes \
- + -a "x$ac_cv_have_libressl_write_3_arg" = xyes; then
- + AM_CONDITIONAL([HAVE_LIBRESSL], true)
- + AM_CONDITIONAL([HAVE_SSL], true)
- + AC_DEFINE([HAVE_LIBRESSL], [yes])
- + AC_DEFINE([HAVE_SSL], [yes])
- +else
- + AC_MSG_WARN([LibreSSL support disabled])
- +fi
- +
- +if test "x$with_openssl" != xno \
- + -a "x$ac_cv_func_X509_STORE_load_locations" = xyes \
- + -a "x$ac_cv_have_openssl_write_3_arg" = xyes; then
- + AM_CONDITIONAL([HAVE_OPENSSL], true)
- + AM_CONDITIONAL([HAVE_SSL], true)
- + AC_DEFINE([HAVE_OPENSSL], [yes])
- + AC_DEFINE([HAVE_SSL], [yes])
- +else
- + AC_MSG_WARN([OpenSSL support disabled])
- fi
-
- # Share test results with automake
- @@ -144,14 +209,6 @@ AC_ARG_WITH([privsep-path],
- )
- AC_SUBST(PRIVSEP_PATH)
-
- -AC_ARG_WITH([cacert],
- - AS_HELP_STRING([--with-cacert=path],
- - [CA certificate location for HTTPS constraint validation]),
- - CONSTRAINT_CA="$withval",
- - CONSTRAINT_CA="/etc/ssl/cert.pem"
- -)
- -AC_DEFINE_UNQUOTED(CONSTRAINT_CA, "$CONSTRAINT_CA", [CA certificate path])
- -
- AC_CONFIG_FILES([
- Makefile
- include/Makefile
- --- a/include/tls.h 2020-07-31 23:00:40.000000000 +0300
- +++ b/include/tls.h 2020-08-01 19:24:29.153594762 +0300
- @@ -1,8 +1,22 @@
- /*
- * Public domain
- * tls.h compatibility shim
- + *
- + * __linux__
- + * __sun
- + * __FreeBSD__
- + * __NetBSD__
- + * __OpenBSD__
- + * __APPLE__
- */
-
- -#ifdef HAVE_LIBTLS
- +#if defined(HAVE_LIBRESSL) && __linux__
- +#include_next <libressl/tls.h>
- +#elif HAVE_LIBRESSL
- #include_next <tls.h>
- #endif
- +
- +#ifdef HAVE_OPENSSL
- +#include <openssl/ssl.h>
- +#include <openssl/err.h>
- +#endif
- --- a/src/constraint.c 2020-08-02 01:57:57.020286149 +0300
- +++ b/src/constraint.c 2020-08-02 01:58:28.366952848 +0300
- @@ -39,7 +39,6 @@
- #include <unistd.h>
- #include <time.h>
- #include <ctype.h>
- -#include <tls.h>
- #include <pwd.h>
- #include <math.h>
-
- @@ -65,33 +64,11 @@ void priv_constraint_close(int, int);
- void priv_constraint_readquery(struct constraint *, struct ntp_addr_msg *,
- uint8_t **);
-
- -struct httpsdate *
- - httpsdate_init(const char *, const int *, 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,
- - struct timeval *, struct timeval *);
- -
- -char *tls_readline(struct tls *, size_t *, size_t *, struct timeval *);
- -
- u_int constraint_cnt;
- extern u_int peer_cnt;
- extern struct imsgbuf *ibuf; /* priv */
- extern struct imsgbuf *ibuf_main; /* chld */
-
- -struct httpsdate {
- - char *tls_addr;
- - char *tls_port;
- - char *tls_hostname;
- - char *tls_path;
- - char *tls_request;
- - struct tls_config *tls_config;
- - struct tls *tls_ctx;
- - struct tm tls_tm;
- -};
- -
- int
- constraint_init(struct constraint *cstr)
- {
- @@ -155,7 +132,7 @@ constraint_query(struct constraint *cstr
- {
- time_t now;
- struct ntp_addr_msg am;
- - struct iovec iov[3];
- + struct iovec iov[4];
- int iov_cnt = 0;
-
- now = getmonotime();
- @@ -252,7 +229,7 @@ priv_constraint_msg(u_int32_t id, u_int8
- 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)) {
- log_warnx("constraint id %d: invalid query received", id);
- return;
- }
- @@ -343,13 +320,13 @@ priv_constraint_readquery(struct constra
- memcpy(h, &am->a, sizeof(*h));
- memcpy(&port, &am->port, sizeof(port));
- h->next = NULL;
- -
- +
- cstr->id = imsg.hdr.peerid;
- cstr->addr = h;
- cstr->addr_head.a = h;
- cstr->port = port;
- cstr->addr_head.port = port;
- -
- +
- dptr = imsg.data;
- memcpy(*data, dptr + sizeof(*am), mlen - sizeof(*am));
- imsg_free(&imsg);
- @@ -364,20 +341,46 @@ priv_constraint_child(const char *pw_dir
- static char addr[NI_MAXHOST];
- struct timeval rectv, xmttv;
- struct sigaction sa;
- - void *ctx;
- + void *ctx = NULL;
- struct iovec iov[2];
- int i, rv;
- +#ifdef HAVE_OPENSSL
- + X509_STORE *o_store = NULL;
- +#endif
-
- if (setpriority(PRIO_PROCESS, 0, 0) == -1)
- log_warn("constraint: can't set priority for subprocess");
-
- -#ifdef HAVE_LIBTLS
- - /* Init TLS and load CA certs before chroot() */
- - if (tls_init() == -1)
- - fatalx("constraint: can't initialize LibreSSL engine");
- - if ((conf->ca = tls_load_file(tls_default_ca_cert_file(),
- - &conf->ca_len, NULL)) == NULL)
- - log_warnx("constraint: failed to load CA certificate bundle file");
- +/* Init TLS and load CA certs before chroot() */
- +#ifdef HAVE_LIBRESSL
- + if (strcmp("libressl", conf->constraint_engine) == 0) {
- + if (tls_init() == -1)
- + fatalx("constraint: can't initialize LibreSSL engine");
- + if (conf->constraint_ca_validation == 1) {
- + if ((conf->ca = tls_load_file(conf->constraint_ca,
- + &conf->ca_len, NULL)) == NULL)
- + log_warnx("constraint: failed to load CA certificate bundle file");
- + }
- + }
- +#endif
- +
- +#ifdef HAVE_OPENSSL
- + if (strcmp("openssl", conf->constraint_engine) == 0) {
- + if (OPENSSL_init_ssl(0, NULL) == 0)
- + fatalx("constraint: can't initialize OpenSSL engine");
- + //SSL_library_init();
- + OpenSSL_add_all_algorithms();
- + OpenSSL_add_all_digests();
- + SSL_load_error_strings();
- + o_store = X509_STORE_new();
- +
- + if (conf->constraint_ca_validation == 1) {
- + if ((conf->o_ca = X509_STORE_load_locations(o_store, conf->constraint_ca, NULL)) != 1) {
- + log_warnx("constraint: failed to load CA certificate bundle file");
- + openssl_lasterr();
- + }
- + }
- + }
- #endif
-
- if (chroot(pw_dir) == -1)
- @@ -420,7 +423,13 @@ priv_constraint_child(const char *pw_dir
-
- log_debug("constraint %s: setting HTTPS request", addr);
- setproctitle("constraint %s: new HTTPS request", addr);
- - (void)closefrom(CONSTRAINT_PASSFD + 1);
- +
- + /*
- + * OpenSSL requires new file descriptors which must not be deleted.
- + * This restriction does not apply to LibreSSL implementation.
- + */
- + if (strcmp("libressl", conf->constraint_engine) == 0)
- + (void)closefrom(CONSTRAINT_PASSFD + 1);
-
- /*
- * Set the close-on-exec flag to prevent leaking the communication
- @@ -449,14 +458,32 @@ priv_constraint_child(const char *pw_dir
- fatalx("constraint %s: invalid port", addr);
- }
-
- - /* Run! */
- - 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) {
- - log_debug("constraint %s: failed to get proper time results", addr);
- - /* Abort with failure but without warning */
- - exit(1);
- +#ifdef HAVE_LIBRESSL
- + if (strcmp("libressl", conf->constraint_engine) == 0) {
- + /* Run! */
- + 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) {
- + log_debug("constraint %s: failed to get proper time results", addr);
- + /* Abort with failure but without warning */
- + exit(1);
- + }
- }
- +#endif
- +
- +#ifdef HAVE_OPENSSL
- + if (strcmp("openssl", conf->constraint_engine) == 0) {
- + /* Run! */
- + log_debug("constraint %s: initializing HTTPS request", addr);
- + if ((ctx = o_httpsdate_query(&cstr,
- + &conf->o_ca, &rectv, &xmttv)) == NULL) {
- + log_debug("constraint %s: failed to get proper time results", addr);
- + /* Abort with failure but without warning */
- + exit(1);
- + }
- + }
- +#endif
-
- iov[0].iov_base = &rectv;
- iov[0].iov_len = sizeof(rectv);
- @@ -468,8 +495,18 @@ priv_constraint_child(const char *pw_dir
- rv = imsg_flush(&cstr.ibuf);
- } while (rv == -1 && errno == EAGAIN);
-
- - /* Tear down the TLS connection after sending the result */
- - httpsdate_free(ctx);
- +/* Tear down the TLS connection after sending the result */
- +#ifdef HAVE_LIBRESSL
- + if (strcmp("libressl", conf->constraint_engine) == 0) {
- + httpsdate_free(ctx);
- + }
- +#endif
- +
- +#ifdef HAVE_OPENSSL
- + if (strcmp("openssl", conf->constraint_engine) == 0) {
- + o_httpsdate_free(ctx);
- + }
- +#endif
-
- exit(0);
- }
- @@ -932,270 +969,6 @@ constraint_check(double val)
- return (0);
- }
-
- -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)
- -{
- - struct httpsdate *httpsdate = NULL;
- - char port_s[sizeof(port)];
- -
- - if ((httpsdate = calloc(1, sizeof(*httpsdate))) == NULL)
- - goto fail;
- -
- - if (hostname == NULL)
- - hostname = addr;
- -
- - sprintf(port_s, "%d", *port);
- -
- - if ((httpsdate->tls_addr = strdup(addr)) == NULL ||
- - (httpsdate->tls_port = strdup(port_s)) == NULL ||
- - (httpsdate->tls_hostname = strdup(hostname)) == NULL ||
- - (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 ((httpsdate->tls_config = tls_config_new()) == NULL)
- - goto fail;
- - if (tls_config_set_ca_mem(httpsdate->tls_config, ca, ca_len) == -1)
- - goto fail;
- -
- - /*
- - * Due to the fact that we're trying to determine a constraint for time
- - * we do our own certificate validity checking, since the automatic
- - * version is based on our wallclock, which may well be inaccurate...
- - */
- - tls_config_insecure_noverifytime(httpsdate->tls_config);
- -
- - return (httpsdate);
- -
- - fail:
- - httpsdate_free(httpsdate);
- - return (NULL);
- -}
- -
- -void
- -httpsdate_free(void *arg)
- -{
- - struct httpsdate *httpsdate = arg;
- - if (httpsdate == NULL)
- - return;
- - if (httpsdate->tls_ctx)
- - tls_close(httpsdate->tls_ctx);
- - tls_free(httpsdate->tls_ctx);
- - tls_config_free(httpsdate->tls_config);
- - free(httpsdate->tls_addr);
- - free(httpsdate->tls_port);
- - free(httpsdate->tls_hostname);
- - free(httpsdate->tls_path);
- - free(httpsdate->tls_request);
- - free(httpsdate);
- -}
- -
- -int
- -httpsdate_request(struct httpsdate *httpsdate, struct timeval *when)
- -{
- - char timebuf1[32], timebuf2[32];
- - size_t outlen = 0, maxlength = conf->constraint_maxheaderlength, len;
- - char *line, *p, *buf;
- - time_t httptime, notbefore, notafter;
- - struct tm *tm;
- - ssize_t ret;
- -
- - if ((httpsdate->tls_ctx = tls_client()) == NULL)
- - goto fail;
- -
- - if (tls_configure(httpsdate->tls_ctx, httpsdate->tls_config) == -1)
- - goto fail;
- -
- - /*
- - * libtls 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
- - * does not trigger any DNS operation and is safe to be called
- - * without the dns pledge.
- - */
- - log_debug("constraint %s: establishing connection", httpsdate->tls_addr);
- - if (tls_connect_servername(httpsdate->tls_ctx, httpsdate->tls_addr,
- - httpsdate->tls_port, httpsdate->tls_hostname) == -1) {
- - log_debug("constraint %s: TLS connection failed (%s): %s",
- - httpsdate->tls_addr,
- - httpsdate->tls_hostname,
- - tls_error(httpsdate->tls_ctx)
- - );
- - goto fail;
- - }
- -
- - buf = httpsdate->tls_request;
- - len = strlen(httpsdate->tls_request);
- - while (len > 0) {
- - ret = tls_write(httpsdate->tls_ctx, buf, len);
- - if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT)
- - continue;
- - if (ret == -1) {
- - log_warnx("constraint %s: TLS write operation failed (%s): %s",
- - httpsdate->tls_addr,
- - httpsdate->tls_hostname,
- - tls_error(httpsdate->tls_ctx)
- - );
- - goto fail;
- - }
- - buf += ret;
- - len -= ret;
- - }
- -
- - while ((line = tls_readline(httpsdate->tls_ctx, &outlen,
- - &maxlength, when)) != NULL) {
- - line[strcspn(line, "\r\n")] = '\0';
- -
- - if ((p = strchr(line, ' ')) == NULL || *p == '\0')
- - goto next;
- - *p++ = '\0';
- - if (strcasecmp("Date:", line) != 0)
- - goto next;
- -
- - /*
- - * Expect the date/time format as IMF-fixdate which is
- - * mandated by HTTP/1.1 in the new RFC 7231 and was
- - * preferred by RFC 2616. Other formats would be RFC 850
- - * or ANSI C's asctime() - the latter doesn't include
- - * the timezone which is required here.
- - */
- - if (strptime(p, IMF_FIXDATE,
- - &httpsdate->tls_tm) == NULL) {
- - log_warnx("constraint %s: unsupported date format",
- - httpsdate->tls_addr
- - );
- - free(line);
- - return (-1);
- - }
- -
- - free(line);
- - break;
- - next:
- - free(line);
- - }
- -
- - /*
- - * Now manually check the validity of the certificate presented in the
- - * TLS handshake, based on the time specified by the server's HTTP Date:
- - * header.
- - */
- - notbefore = tls_peer_cert_notbefore(httpsdate->tls_ctx);
- - notafter = tls_peer_cert_notafter(httpsdate->tls_ctx);
- - if ((httptime = timegm(&httpsdate->tls_tm)) == -1)
- - goto fail;
- - if (httptime <= notbefore) {
- - if ((tm = gmtime(¬before)) == NULL)
- - goto fail;
- - if (strftime(timebuf1, sizeof(timebuf1), X509_DATE, tm) == 0)
- - goto fail;
- - if (strftime(timebuf2, sizeof(timebuf2), X509_DATE,
- - &httpsdate->tls_tm) == 0)
- - goto fail;
- - log_warnx("constraint %s: TLS certificate not yet valid (%s): "
- - "not before %s, now is %s", httpsdate->tls_addr,
- - httpsdate->tls_hostname, timebuf1, timebuf2);
- - goto fail;
- - }
- - if (httptime >= notafter) {
- - if ((tm = gmtime(¬after)) == NULL)
- - goto fail;
- - if (strftime(timebuf1, sizeof(timebuf1), X509_DATE, tm) == 0)
- - goto fail;
- - if (strftime(timebuf2, sizeof(timebuf2), X509_DATE,
- - &httpsdate->tls_tm) == 0)
- - goto fail;
- - log_warnx("constraint %s: TLS certificate has been expired (%s): "
- - "not after %s, now is %s", httpsdate->tls_addr,
- - httpsdate->tls_hostname, timebuf1, timebuf2);
- - goto fail;
- - }
- -
- - return (0);
- -
- - fail:
- - httpsdate_free(httpsdate);
- - return (-1);
- -}
- -
- -void *
- -httpsdate_query(const char *addr, const int *port, const char *hostname,
- - const char *path, 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,
- - ca, ca_len)) == NULL)
- - return (NULL);
- -
- - if (httpsdate_request(httpsdate, &when) == -1)
- - return (NULL);
- -
- - /* Return parsed date as local time */
- - t = timegm(&httpsdate->tls_tm);
- -
- - /* Report parsed Date: as "received time" */
- - rectv->tv_sec = t;
- - rectv->tv_usec = 0;
- -
- - /* And add delay as "transmit time" */
- - xmttv->tv_sec = when.tv_sec;
- - xmttv->tv_usec = when.tv_usec;
- -
- - return (httpsdate);
- -}
- -
- -/* Based on SSL_readline in ftp/fetch.c */
- -char *
- -tls_readline(struct tls *tls, size_t *lenp, size_t *maxlength,
- - struct timeval *when)
- -{
- - size_t i, len;
- - char *buf, *q, c;
- - ssize_t ret;
- -
- - len = 128;
- - if ((buf = malloc(len)) == NULL)
- - fatal("constraint: can't allocate memory for TLS transfer buffer");
- - for (i = 0; ; i++) {
- - if (i >= len - 1) {
- - if ((q = reallocarray(buf, len, 2)) == NULL)
- - fatal("constraint: can't expand TLS transfer buffer");
- - buf = q;
- - len *= 2;
- - }
- - again:
- - ret = tls_read(tls, &c, 1);
- - if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT)
- - goto again;
- - if (ret == -1) {
- - /* SSL read error, ignore */
- - free(buf);
- - return (NULL);
- - }
- -
- - if (maxlength != NULL && (*maxlength)-- == 0) {
- - log_warnx("constraint: maximum HTTP header length exceeded");
- - free(buf);
- - return (NULL);
- - }
- -
- - buf[i] = c;
- - if (c == '\n')
- - break;
- - }
- - *lenp = i;
- - if (gettimeofday(when, NULL) == -1)
- - fatal("constraint: can't get a valid time stamp");
- - return (buf);
- -}
- -
- char *
- get_string(u_int8_t *ptr, size_t len)
- {
- --- /dev/null 2020-07-26 15:23:52.401078754 +0300
- +++ b/src/constraint-openssl.c 2020-08-03 19:23:54.377109002 +0300
- @@ -0,0 +1,329 @@
- +/*
- + * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
- + * Copyright (c) 2020 Pekka Helenius <fincer89@hotmail.com>
- + *
- + * Permission to use, copy, modify, and distribute this software for any
- + * purpose with or without fee is hereby granted, provided that the above
- + * copyright notice and this permission notice appear in all copies.
- + *
- + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- + */
- +
- +#include <errno.h>
- +#include <stdio.h>
- +#include <stdlib.h>
- +#include <string.h>
- +#include <strings.h>
- +#include <time.h>
- +#include <sys/socket.h>
- +#include <unistd.h>
- +
- +#include "ntpd.h"
- +
- +struct o_httpsdate *
- +o_httpsdate_init(struct constraint *cstr, const int *ca)
- +{
- + struct o_httpsdate *httpsdate = NULL;
- +
- + if ((httpsdate = calloc(1, sizeof(*httpsdate))) == NULL)
- + goto fail;
- +
- + 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->tls_method = TLS_method()) == NULL)
- + goto fail;
- +
- + if ((httpsdate->tls_ctx = SSL_CTX_new(httpsdate->tls_method)) == NULL)
- + goto fail;
- +
- + if ((SSL_CTX_set_cipher_list(httpsdate->tls_ctx,
- + SSL_DEFAULT_CIPHER_LIST)) <= 0)
- + goto fail;
- +
- + if (*ca == 1 || conf->constraint_ca_validation == 0) {
- + SSL_CTX_set_verify(httpsdate->tls_ctx, SSL_VERIFY_NONE, NULL);
- + } else {
- + /*
- + * OpenSSL built-in procedure terminates connection in
- + * a case of verification failure if SSL_VERIFY_PEER
- + * is used on the client side with NULL value for
- + * verify_callback function.
- + * See man SSL_CTX_set_verify for details.
- + */
- + SSL_CTX_set_verify(httpsdate->tls_ctx, SSL_VERIFY_PEER, NULL);
- + }
- +
- + return (httpsdate);
- +
- + fail:
- + o_httpsdate_free(httpsdate);
- + return (NULL);
- +}
- +
- +void
- +o_httpsdate_free(void *arg)
- +{
- + struct o_httpsdate *httpsdate = arg;
- + if (httpsdate == NULL)
- + return;
- + if (httpsdate->tls_conn) {
- + SSL_shutdown(httpsdate->tls_conn);
- + SSL_free(httpsdate->tls_conn);
- + }
- + SSL_CTX_free(httpsdate->tls_ctx);
- + free(httpsdate->tls_request);
- + free(httpsdate);
- +}
- +
- +int
- +o_httpsdate_request(struct o_httpsdate *httpsdate, struct timeval *when)
- +{
- + size_t outlen = 0, maxlength = conf->constraint_max_headerlength, len;
- + struct sockaddr_in sa_in;
- + struct sockaddr_in6 *sa_in6;
- + struct ntp_addr *h;
- + char *line, *p, *buf;
- + int ret, sslfd = 0;
- + char ia_str[70]; // 33: 32-bit IPv4 + EOL :: 65: 64-bit IPv6 + EOL
- +
- + if ((httpsdate->tls_conn = SSL_new(httpsdate->tls_ctx)) == NULL)
- + goto fail;
- +
- + h = httpsdate->cstr->addr;
- +
- + switch (h->ss.ss_family) {
- +
- + case AF_INET:
- + memset(&sa_in, 0, sizeof(sa_in));
- + memcpy(&sa_in, (struct sockaddr_in *)&h->ss, sizeof(sa_in));
- +
- + inet_ntop(AF_INET, &sa_in.sin_addr, ia_str, sizeof(ia_str));
- +
- + if ((sslfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) == -1)
- + log_warnx("constraint %s: can't create OpenSSL socket (4)", ia_str);
- +
- + SSL_set_fd(httpsdate->tls_conn, sslfd);
- +
- + log_debug("constraint %s: initializing HTTPS request", ia_str);
- + ret = connect(sslfd, (struct sockaddr *)&sa_in, sizeof(sa_in));
- + if (ret < 0) {
- + log_warnx("constraint %s: TLS connection failed, socket error: %d",
- + ia_str,
- + errno
- + );
- + goto fail;
- + }
- +
- + break;
- +
- + case AF_INET6:
- + memset(&sa_in6, 0, sizeof(sa_in6));
- + memcpy(&sa_in6, (struct sockaddr6_in *)&h->ss, sizeof(sa_in6));
- +
- + inet_ntop(AF_INET6, &sa_in6->sin6_addr, ia_str, sizeof(ia_str));
- +
- + if ((sslfd = socket(AF_INET6, SOCK_STREAM, 0)) == -1)
- + log_warnx("constraint %s: can't create OpenSSL socket (6)",
- + ia_str
- + );
- +
- + SSL_set_fd(httpsdate->tls_conn, sslfd);
- +
- + log_debug("constraint %s: initializing HTTPS request", ia_str);
- + ret = connect(sslfd, (struct sockaddr *)&sa_in6, sizeof(sa_in6));
- + if (ret < 0) {
- + log_warnx("constraint %s: TLS connection failed, socket error: %d",
- + ia_str,
- + errno
- + );
- + goto fail;
- + }
- +
- + break;
- +
- + default:
- + fatalx("constraint %s: invalid network address family in HTTPS request",
- + ia_str
- + );
- + goto fail;
- + }
- +
- + log_debug("constraint %s: establishing connection", ia_str);
- + ret = SSL_connect(httpsdate->tls_conn);
- + if (ret < 1) {
- + log_warnx("constraint %s: TLS connection failed in accept (%s)",
- + ia_str,
- + httpsdate->cstr->addr_head.name
- + );
- + openssl_lasterr();
- + goto fail;
- + }
- + if (ret == 5) {
- + log_warnx("constraint %s: socket error no: %d", ia_str, ret);
- + goto fail;
- + }
- +
- + buf = httpsdate->tls_request;
- + len = strlen(httpsdate->tls_request);
- +
- + while (len > 0) {
- + ret = SSL_write(httpsdate->tls_conn, buf, len);
- + if (ret == SSL_ERROR_WANT_WRITE || ret == SSL_ERROR_WANT_READ)
- + continue;
- + if (ret < 1) {
- + log_warnx("constraint %s: TLS write operation failed: (%s)",
- + ia_str,
- + httpsdate->cstr->addr_head.name
- + );
- + openssl_lasterr();
- + goto fail;
- + }
- + if (ret == 6) {
- + log_warnx("constraint %s: HTTPS server aborted the request", ia_str);
- + openssl_lasterr();
- + SSL_shutdown(httpsdate->tls_conn);
- + }
- + buf += ret;
- + len -= ret;
- + }
- +
- + while ((line = o_tls_readline(httpsdate->tls_conn, &outlen,
- + &maxlength, when)) != NULL) {
- +
- + line[strcspn(line, "\r\n")] = '\0';
- +
- + if ((p = strchr(line, ' ')) == NULL || *p == '\0')
- + goto next;
- + *p++ = '\0';
- + if (strcasecmp("Date:", line) != 0)
- + goto next;
- +
- + /*
- + * Expect the date/time format as IMF-fixdate which is
- + * mandated by HTTP/1.1 in the new RFC 7231 and was
- + * preferred by RFC 2616. Other formats would be RFC 850
- + * or ANSI C's asctime() - the latter doesn't include
- + * the timezone which is required here.
- + */
- + if (strptime(p, IMF_FIXDATE,
- + &httpsdate->tls_tm) == NULL) {
- + log_warnx("constraint %s: unsupported date format",
- + ia_str
- + );
- + free(line);
- + return (-1);
- + }
- +
- + free(line);
- + break;
- + next:
- + free(line);
- + }
- +
- + return (0);
- +
- + fail:
- + close(sslfd);
- + o_httpsdate_free(httpsdate);
- + return (-1);
- +}
- +
- +void *
- +o_httpsdate_query(struct constraint *cstr, const int *ca,
- + struct timeval *rectv, struct timeval *xmttv)
- +{
- + struct o_httpsdate *httpsdate;
- + struct timeval when;
- + time_t t;
- +
- + if ((httpsdate = o_httpsdate_init(cstr, ca)) == NULL)
- + return (NULL);
- +
- + if (o_httpsdate_request(httpsdate, &when) == -1)
- + return (NULL);
- +
- + /* Return parsed date as local time */
- + t = timegm(&httpsdate->tls_tm);
- +
- + /* Report parsed Date: as "received time" */
- + rectv->tv_sec = t;
- + rectv->tv_usec = 0;
- +
- + /* And add delay as "transmit time" */
- + xmttv->tv_sec = when.tv_sec;
- + xmttv->tv_usec = when.tv_usec;
- +
- + return (httpsdate);
- +}
- +
- +/* Based on SSL_readline in ftp/fetch.c */
- +char *
- +o_tls_readline(SSL *tls, size_t *lenp, size_t *maxlength,
- + struct timeval *when)
- +{
- +
- + size_t i, len;
- + char *buf, *q, c;
- + ssize_t ret;
- +
- + len = 128;
- + if ((buf = malloc(len)) == NULL)
- + fatal("constraint: can't allocate memory for TLS transfer buffer");
- +
- + for (i = 0; ; i++) {
- + if (i >= len - 1) {
- + if ((q = reallocarray(buf, len, 2)) == NULL)
- + fatal("constraint: can't expand TLS transfer buffer");
- + buf = q;
- + len *= 2;
- + }
- + again:
- + ret = SSL_read(tls, &c, 1);
- + if (ret == SSL_ERROR_WANT_WRITE || ret == SSL_ERROR_WANT_READ)
- + goto again;
- + if (ret < 1 || ret == 6) {
- + /* SSL read error, ignore */
- + free(buf);
- + return (NULL);
- + }
- +
- + if (maxlength != NULL && (*maxlength)-- == 0) {
- + log_warnx("constraint: maximum HTTP header length exceeded");
- + free(buf);
- + return (NULL);
- + }
- +
- + buf[i] = c;
- + if (c == '\n')
- + break;
- + }
- + *lenp = i;
- + if (gettimeofday(when, NULL) == -1)
- + fatal("constraint: can't get a valid time stamp");
- + return (buf);
- +}
- +
- +void
- +openssl_lasterr(void)
- +{
- + unsigned long err = ERR_peek_last_error();
- + char buf[2048];
- + char *msg;
- +
- + msg = ERR_error_string(err, buf);
- + ERR_clear_error();
- + log_warnx("%s", msg);
- +}
- --- /dev/null 2020-07-26 15:23:52.401078754 +0300
- +++ b/src/constraint-libressl.c 2020-08-01 19:50:24.130263065 +0300
- @@ -0,0 +1,296 @@
- +/* $OpenBSD: constraint.c,v 1.35 2016/12/05 10:41:33 rzalamena Exp $ */
- +
- +/*
- + * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
- + *
- + * Permission to use, copy, modify, and distribute this software for any
- + * purpose with or without fee is hereby granted, provided that the above
- + * copyright notice and this permission notice appear in all copies.
- + *
- + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- + */
- +
- +#include <stdio.h>
- +#include <stdlib.h>
- +#include <string.h>
- +#include <strings.h>
- +#include <time.h>
- +
- +#include "ntpd.h"
- +
- +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)
- +{
- + struct httpsdate *httpsdate = NULL;
- + char port_s[sizeof(port)];
- +
- + if ((httpsdate = calloc(1, sizeof(*httpsdate))) == NULL)
- + goto fail;
- +
- + if (hostname == NULL)
- + hostname = addr;
- +
- + sprintf(port_s, "%d", *port);
- +
- + if ((httpsdate->tls_addr = strdup(addr)) == NULL ||
- + (httpsdate->tls_port = strdup(port_s)) == NULL ||
- + (httpsdate->tls_hostname = strdup(hostname)) == NULL ||
- + (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 ((httpsdate->tls_config = tls_config_new()) == NULL)
- + goto fail;
- +
- + if (ca == NULL || ca_len == 0 || conf->constraint_ca_validation == 0) {
- + log_debug("constraint %s: certificate can't be verified", addr);
- + tls_config_insecure_noverifycert(httpsdate->tls_config);
- + } else {
- + log_debug("constraint %s: verifying certificate", addr);
- + if (tls_config_set_ca_mem(httpsdate->tls_config, ca, ca_len) == -1)
- + goto fail;
- + }
- +
- + /*
- + * Due to the fact that we're trying to determine a constraint for time
- + * we do our own certificate validity checking, since the automatic
- + * version is based on our wallclock, which may well be inaccurate...
- + */
- + tls_config_insecure_noverifytime(httpsdate->tls_config);
- +
- + return (httpsdate);
- +
- + fail:
- + httpsdate_free(httpsdate);
- + return (NULL);
- +}
- +
- +void
- +httpsdate_free(void *arg)
- +{
- + struct httpsdate *httpsdate = arg;
- + if (httpsdate == NULL)
- + return;
- + if (httpsdate->tls_ctx)
- + tls_close(httpsdate->tls_ctx);
- + tls_free(httpsdate->tls_ctx);
- + tls_config_free(httpsdate->tls_config);
- + free(httpsdate->tls_addr);
- + free(httpsdate->tls_port);
- + free(httpsdate->tls_hostname);
- + free(httpsdate->tls_path);
- + free(httpsdate->tls_request);
- + free(httpsdate);
- +}
- +
- +int
- +httpsdate_request(struct httpsdate *httpsdate, struct timeval *when)
- +{
- + char timebuf1[32], timebuf2[32];
- + size_t outlen = 0, maxlength = conf->constraint_max_headerlength, len;
- + char *line, *p, *buf;
- + time_t httptime, notbefore, notafter;
- + struct tm *tm;
- + ssize_t ret;
- +
- + if ((httpsdate->tls_ctx = tls_client()) == NULL)
- + goto fail;
- +
- + if (tls_configure(httpsdate->tls_ctx, httpsdate->tls_config) == -1)
- + goto fail;
- +
- + /*
- + * 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
- + * does not trigger any DNS operation and is safe to be called
- + * without the dns pledge.
- + */
- + log_debug("constraint %s: establishing connection", httpsdate->tls_addr);
- + if (tls_connect_servername(httpsdate->tls_ctx, httpsdate->tls_addr,
- + httpsdate->tls_port, httpsdate->tls_hostname) == -1) {
- + log_debug("constraint %s: TLS connection failed (%s): %s",
- + httpsdate->tls_addr,
- + httpsdate->tls_hostname,
- + tls_error(httpsdate->tls_ctx)
- + );
- + goto fail;
- + }
- +
- + buf = httpsdate->tls_request;
- + len = strlen(httpsdate->tls_request);
- + while (len > 0) {
- + ret = tls_write(httpsdate->tls_ctx, buf, len);
- + if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT)
- + continue;
- + if (ret == -1) {
- + log_warnx("constraint %s: TLS write operation failed (%s): %s",
- + httpsdate->tls_addr,
- + httpsdate->tls_hostname,
- + tls_error(httpsdate->tls_ctx)
- + );
- + goto fail;
- + }
- + buf += ret;
- + len -= ret;
- + }
- +
- + while ((line = tls_readline(httpsdate->tls_ctx, &outlen,
- + &maxlength, when)) != NULL) {
- + line[strcspn(line, "\r\n")] = '\0';
- +
- + if ((p = strchr(line, ' ')) == NULL || *p == '\0')
- + goto next;
- + *p++ = '\0';
- + if (strcasecmp("Date:", line) != 0)
- + goto next;
- +
- + /*
- + * Expect the date/time format as IMF-fixdate which is
- + * mandated by HTTP/1.1 in the new RFC 7231 and was
- + * preferred by RFC 2616. Other formats would be RFC 850
- + * or ANSI C's asctime() - the latter doesn't include
- + * the timezone which is required here.
- + */
- + if (strptime(p, IMF_FIXDATE,
- + &httpsdate->tls_tm) == NULL) {
- + log_warnx("constraint %s: unsupported date format",
- + httpsdate->tls_addr
- + );
- + free(line);
- + return (-1);
- + }
- +
- + free(line);
- + break;
- + next:
- + free(line);
- + }
- +
- + /*
- + * Now manually check the validity of the certificate presented in the
- + * TLS handshake, based on the time specified by the server's HTTP Date:
- + * header.
- + */
- + notbefore = tls_peer_cert_notbefore(httpsdate->tls_ctx);
- + notafter = tls_peer_cert_notafter(httpsdate->tls_ctx);
- + if ((httptime = timegm(&httpsdate->tls_tm)) == -1)
- + goto fail;
- + if (httptime <= notbefore) {
- + if ((tm = gmtime(¬before)) == NULL)
- + goto fail;
- + if (strftime(timebuf1, sizeof(timebuf1), X509_DATE, tm) == 0)
- + goto fail;
- + if (strftime(timebuf2, sizeof(timebuf2), X509_DATE,
- + &httpsdate->tls_tm) == 0)
- + goto fail;
- + log_warnx("constraint %s: TLS certificate not yet valid (%s): "
- + "not before %s, now is %s", httpsdate->tls_addr,
- + httpsdate->tls_hostname, timebuf1, timebuf2);
- + goto fail;
- + }
- + if (httptime >= notafter) {
- + if ((tm = gmtime(¬after)) == NULL)
- + goto fail;
- + if (strftime(timebuf1, sizeof(timebuf1), X509_DATE, tm) == 0)
- + goto fail;
- + if (strftime(timebuf2, sizeof(timebuf2), X509_DATE,
- + &httpsdate->tls_tm) == 0)
- + goto fail;
- + log_warnx("constraint %s: TLS certificate has been expired (%s): "
- + "not after %s, now is %s", httpsdate->tls_addr,
- + httpsdate->tls_hostname, timebuf1, timebuf2);
- + goto fail;
- + }
- +
- + return (0);
- +
- + fail:
- + httpsdate_free(httpsdate);
- + return (-1);
- +}
- +
- +void *
- +httpsdate_query(const char *addr, const int *port, const char *hostname,
- + const char *path, 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,
- + ca, ca_len)) == NULL)
- + return (NULL);
- +
- + if (httpsdate_request(httpsdate, &when) == -1)
- + return (NULL);
- +
- + /* Return parsed date as local time */
- + t = timegm(&httpsdate->tls_tm);
- +
- + /* Report parsed Date: as "received time" */
- + rectv->tv_sec = t;
- + rectv->tv_usec = 0;
- +
- + /* And add delay as "transmit time" */
- + xmttv->tv_sec = when.tv_sec;
- + xmttv->tv_usec = when.tv_usec;
- +
- + return (httpsdate);
- +}
- +
- +/* Based on SSL_readline in ftp/fetch.c */
- +char *
- +tls_readline(struct tls *tls, size_t *lenp, size_t *maxlength,
- + struct timeval *when)
- +{
- + size_t i, len;
- + char *buf, *q, c;
- + ssize_t ret;
- +
- + len = 128;
- + if ((buf = malloc(len)) == NULL)
- + fatal("constraint: can't allocate memory for TLS transfer buffer");
- + for (i = 0; ; i++) {
- + if (i >= len - 1) {
- + if ((q = reallocarray(buf, len, 2)) == NULL)
- + fatal("constraint: can't expand TLS transfer buffer");
- + buf = q;
- + len *= 2;
- + }
- + again:
- + ret = tls_read(tls, &c, 1);
- + if (ret == TLS_WANT_POLLIN || ret == TLS_WANT_POLLOUT)
- + goto again;
- + if (ret == -1) {
- + /* SSL read error, ignore */
- + free(buf);
- + return (NULL);
- + }
- +
- + if (maxlength != NULL && (*maxlength)-- == 0) {
- + log_warnx("constraint: maximum HTTP header length exceeded");
- + free(buf);
- + return (NULL);
- + }
- +
- + buf[i] = c;
- + if (c == '\n')
- + break;
- + }
- + *lenp = i;
- + if (gettimeofday(when, NULL) == -1)
- + fatal("constraint: can't get a valid time stamp");
- + return (buf);
- +}
- --- a/src/config.c 2020-08-01 11:35:05.758097319 +0300
- +++ b/src/config.c 2020-08-01 19:37:13.476928898 +0300
- @@ -190,9 +190,6 @@ new_constraint(void)
- p->id = ++constraint_maxid;
- p->fd = -1;
-
- -#ifndef HAVE_LIBTLS
- - log_warnx("constraint configured without LibreSSL support");
- -#endif
- return (p);
- }
-
- --- a/src/ntpd.h 2020-08-03 23:10:00.839597442 +0300
- +++ b/src/ntpd.h 2020-08-03 23:25:02.978705101 +0300
- @@ -31,6 +31,8 @@
- #include <poll.h>
- #include <imsg.h>
-
- +#include <tls.h>
- +
- #include "ntp.h"
- #include "log.h"
-
- @@ -98,15 +100,29 @@
- #define CONSTRAINT_SCAN_TIMEOUT (10)
- #define CONSTRAINT_MARGIN (2.0*60)
-
- -#define CONSTRAINT_MAXHEADERLENGTH 8192
- +#if defined HAVE_LIBRESSL && !defined(HAVE_OPENSSL)
- +#define CONSTRAINT_ENGINE "libressl"
- +#elif defined HAVE_OPENSSL && !defined(HAVE_LIBRESSL)
- +#define CONSTRAINT_ENGINE "openssl"
- +#else
- +#define CONSTRAINT_ENGINE "libressl"
- +#endif
- +
- +#define CONSTRAINT_MAX_HEADERLENGTH 8192
- #define CONSTRAINT_PASSFD (STDERR_FILENO + 1)
-
- +#define CONSTRAINT_CA SYSCONFDIR "/ssl/cert.pem"
- +#define CONSTRAINT_CA_VALIDATION 0x01;
- +
- #define PARENT_SOCK_FILENO CONSTRAINT_PASSFD
-
- #define NTP_PROC_NAME "ntp_main"
- #define NTPDNS_PROC_NAME "ntp_dns"
- #define CONSTRAINT_PROC_NAME "constraint"
-
- +#define IMF_FIXDATE "%a, %d %h %Y %T GMT"
- +#define X509_DATE "%Y-%m-%d %T UTC"
- +
- enum client_state {
- STATE_NONE,
- STATE_DNS_INPROGRESS,
- @@ -268,6 +284,7 @@ struct ntpd_conf {
- u_int constraint_errors;
- u_int8_t *ca;
- size_t ca_len;
- + int o_ca;
- int tmpfail;
- char *pid_file;
-
- @@ -322,7 +339,10 @@ struct ntpd_conf {
- int constraint_scan_timeout;
- double constraint_margin;
-
- - int constraint_maxheaderlength;
- + char *constraint_engine;
- + int constraint_max_headerlength;
- + char *constraint_ca;
- + u_int8_t constraint_ca_validation;
- };
-
- struct ctl_show_status {
- @@ -466,6 +486,55 @@ void priv_constraint_check_child(pid_t,
- char *get_string(u_int8_t *, size_t);
- int intlen(int);
-
- +#ifdef HAVE_LIBRESSL
- +/* constraint.c */
- +/* constraint-libressl.c */
- +struct httpsdate {
- + char *tls_addr;
- + char *tls_port;
- + char *tls_hostname;
- + char *tls_path;
- + char *tls_request;
- + struct tls_config *tls_config;
- + struct tls *tls_ctx;
- + struct tm tls_tm;
- +};
- +
- +struct httpsdate *
- + httpsdate_init(const char *, const int *, 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,
- + struct timeval *, struct timeval *);
- +
- +char *tls_readline(struct tls *, size_t *, size_t *, struct timeval *);
- +#endif
- +
- +#ifdef HAVE_OPENSSL
- +/* constraint.c */
- +/* constraint-openssl.c */
- +struct o_httpsdate {
- + struct constraint *cstr;
- + char *tls_path;
- + char *tls_request;
- + const SSL_METHOD *tls_method;
- + SSL_CTX *tls_ctx;
- + SSL *tls_conn;
- + struct tm tls_tm;
- +};
- +
- +struct o_httpsdate * o_httpsdate_init(struct constraint *, const int *);
- +void o_httpsdate_free(void *);
- +int o_httpsdate_request(struct o_httpsdate *, struct timeval *);
- +void *o_httpsdate_query(struct constraint *,
- + const int *, struct timeval *, struct timeval *);
- +
- +char *o_tls_readline(SSL *, size_t *, size_t *, struct timeval *);
- +void openssl_lasterr(void);
- +#endif
- +
- /* util.c */
- double gettime_corrected(void);
- double gettime_from_timeval(struct timeval *);
- --- a/src/ntpd.c 2020-08-03 23:48:03.062564686 +0300
- +++ b/src/ntpd.c 2020-08-03 23:48:23.609231373 +0300
- @@ -261,6 +261,20 @@ main(int argc, char *argv[])
- if (lconf.automatic)
- lconf.settime = 1;
-
- +#if __linux__
- + // FIXME
- + /*
- + * Forcefully turn off validation check on Linux due to permission issues.
- + * Use seccomp to fix this?
- + * See https://lwn.net/Articles/767137/
- + */
- + if (conf->constraint_ca_validation == 1) {
- + log_info("warning: turning off constraint certificate validation due to"
- + " insufficient read permissions");
- + conf->constraint_ca_validation = 0;
- + }
- +#endif
- +
- if (pname != NULL) {
- /* Remove our proc arguments, so child doesn't need to. */
- if (sanitize_argv(&argc0, &argv0) == -1)
- @@ -334,8 +348,10 @@ main(int argc, char *argv[])
- * Constraint processes are forked with certificates in memory,
- * then privdrop into chroot before speaking to the outside world.
- */
- - if (unveil(tls_default_ca_cert_file(), "r") == -1)
- - err(1, "main process: can't unveil certificate file for reading");
- + if (conf->constraint_ca_validation == 1) {
- + if (unveil(conf->constraint_ca, "r") == -1)
- + err(1, "main process: can't unveil certificate file for reading");
- + }
- if (unveil("/usr/sbin/ntpd", "x") == -1)
- err(1, "main process: can't unveil ntpd executable for execute operations");
- if (pledge("stdio rpath inet settime proc exec id", NULL) == -1)
- @@ -746,7 +762,6 @@ ctl_main(int argc, char *argv[], const s
-
- memset(&sa, 0, sizeof(sa));
- sa.sun_family = AF_UNIX;
- -
- if (strlcpy(sa.sun_path, sockname, sizeof(sa.sun_path)) >=
- sizeof(sa.sun_path))
- errx(1, "ntpctl: control socket name is too long");
- --- a/src/ntp.c 2020-08-03 23:08:32.397143675 +0300
- +++ b/src/ntp.c 2020-08-03 23:23:48.364816044 +0300
- @@ -166,8 +166,22 @@ ntp_main(struct ntpd_conf *nconf, struct
- constraint_cnt = 0;
- conf->constraint_median = 0;
- conf->constraint_last = getmonotime();
- - TAILQ_FOREACH(cstr, &conf->constraints, entry)
- - constraint_cnt += constraint_init(cstr);
- +
- + if (strcmp("libressl", conf->constraint_engine) == 0)
- +#ifndef HAVE_LIBRESSL
- + warnx("main process: OpenNTPD configured without LibreSSL support");
- +#else
- + TAILQ_FOREACH(cstr, &conf->constraints, entry)
- + constraint_cnt += constraint_init(cstr);
- +#endif
- +
- + if (strcmp("openssl", conf->constraint_engine) == 0)
- +#ifndef HAVE_OPENSSL
- + warnx("main process: OpenNTPD configured without OpenSSL support");
- +#else
- + TAILQ_FOREACH(cstr, &conf->constraints, entry)
- + constraint_cnt += constraint_init(cstr);
- +#endif
-
- TAILQ_FOREACH(p, &conf->ntp_peers, entry)
- client_peer_init(p);
- --- a/src/parse.y 2020-08-03 23:11:12.796264187 +0300
- +++ b/src/parse.y 2020-08-03 23:22:43.401482642 +0300
- @@ -144,7 +144,10 @@ typedef struct {
- %token _CONSTRAINT_SCAN_TIMEOUT
- %token _CONSTRAINT_MARGIN
-
- -%token _CONSTRAINT_MAXHEADERLENGTH
- +%token _CONSTRAINT_ENGINE
- +%token _CONSTRAINT_MAX_HEADERLENGTH
- +%token _CONSTRAINT_CA
- +%token _CONSTRAINT_CA_VALIDATION
-
- %token <v.string> STRING
- %token <v.number> NUMBER
- @@ -580,8 +583,35 @@ main : LISTEN ON address listen_opts {
- | _CONSTRAINT_MARGIN pos_num {
- conf->constraint_margin = (double)$2.pos_num;
- }
- - | _CONSTRAINT_MAXHEADERLENGTH pos_num {
- - conf->constraint_maxheaderlength = $2.pos_num;
- + | _CONSTRAINT_MAX_HEADERLENGTH pos_num {
- + conf->constraint_max_headerlength = $2.pos_num;
- + }
- + | _CONSTRAINT_ENGINE STRING {
- + if (strcmp("libressl", $2) == 0 ||
- + strcmp("openssl", $2) == 0) {
- + conf->constraint_engine = $2;
- + } else {
- + yyerror("Invalid TLS engine specified."
- + " Expected 'libressl' or 'openssl'");
- + YYERROR;
- + }
- + }
- + | _CONSTRAINT_CA STRING {
- + conf->constraint_ca = $2;
- + }
- + | _CONSTRAINT_CA_VALIDATION STRING {
- + u_int8_t val;
- +
- + if (strcmp("true", $2) == 0) {
- + val = 0x01;
- + } else if (strcmp("false", $2) == 0) {
- + val = 0x00;
- + } else {
- + yyerror("option constraint_ca_validation expects either 'true' or 'false'");
- + YYERROR;
- + }
- +
- + conf->constraint_ca_validation = val;
- }
- ;
-
- @@ -842,9 +872,12 @@ lookup(char *s)
- { "auto_replies", _AUTO_REPLIES, "single" },
- { "auto_threshold", _AUTO_THRESHOLD, "single" },
- { "constraint", CONSTRAINT, "multiple" },
- + { "constraint_ca", _CONSTRAINT_CA, "single" },
- + { "constraint_ca_validation", _CONSTRAINT_CA_VALIDATION, "single" },
- + { "constraint_engine", _CONSTRAINT_ENGINE, "single" },
- { "constraint_error_margin", _CONSTRAINT_ERROR_MARGIN, "single" },
- { "constraint_margin", _CONSTRAINT_MARGIN, "single" },
- - { "constraint_maxheaderlength", _CONSTRAINT_MAXHEADERLENGTH, "single" },
- + { "constraint_max_headerlength", _CONSTRAINT_MAX_HEADERLENGTH, "single" },
- { "constraint_retry_interval", _CONSTRAINT_RETRY_INTERVAL, "single" },
- { "constraint_scan_interval", _CONSTRAINT_SCAN_INTERVAL, "single" },
- { "constraint_scan_timeout", _CONSTRAINT_SCAN_TIMEOUT, "single" },
- @@ -1296,7 +1329,10 @@ init_conf(struct ntpd_conf *conf)
- conf->constraint_scan_timeout = CONSTRAINT_SCAN_TIMEOUT; // 10;
- conf->constraint_margin = CONSTRAINT_MARGIN; // 2.0*60;
-
- - conf->constraint_maxheaderlength = CONSTRAINT_MAXHEADERLENGTH; // 8192;
- + conf->constraint_engine = CONSTRAINT_ENGINE; // "libressl";
- + conf->constraint_max_headerlength = CONSTRAINT_MAX_HEADERLENGTH; // 8192;
- + conf->constraint_ca = CONSTRAINT_CA; // /etc/ssl/cert.pem;
- + conf->constraint_ca_validation = CONSTRAINT_CA_VALIDATION; // false;
- }
-
- void
- @@ -1366,7 +1402,16 @@ print_conf(struct ntpd_conf *lconf)
- fprintf(stdout, "Constraint retry interval: %d seconds\n", conf->constraint_retry_interval);
- fprintf(stdout, "Constraint scan interval: %d seconds\n", conf->constraint_scan_interval);
- fprintf(stdout, "Constraint scan timeout: %d seconds\n", conf->constraint_scan_timeout);
- - fprintf(stdout, "Constraint maximum HTTP header length: %d bytes\n", conf->constraint_maxheaderlength);
- + fprintf(stdout, "Constraint TLS engine: %s\n", conf->constraint_engine);
- + fprintf(stdout, "Constraint maximum HTTP header length: %d bytes\n", conf->constraint_max_headerlength);
- + fprintf(stdout, "Constraint certificate file: %s\n", conf->constraint_ca);
- +
- + if ((conf->constraint_ca_validation) == 0x01)
- + *boolean = "true";
- + else if ((conf->constraint_ca_validation) == 0x00)
- + *boolean = "false";
- +
- + fprintf(stdout, "Constraint certificate validation: %s\n", *boolean);
- fprintf(stdout, "\n");
-
- TAILQ_FOREACH(sens, &conf->ntp_conf_sensors, entry) {
- --- a/src/ntpd.conf.5 2020-08-03 23:07:12.770476926 +0300
- +++ b/src/ntpd.conf.5 2020-08-03 23:21:11.124672226 +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 02 2020 $
- +.Dd $Mdocdate: August 01 2020 $
- .Dt NTPD.CONF 5
- .Os
- .Sh NAME
- @@ -235,8 +235,8 @@ invalid.
- .Pp
- Support for constraints is only available if
- .Xr ntpd 8
- -has been linked with libtls from LibreSSL. Configuring a constraint
- -without libtls causes
- +has been linked either with libtls from LibreSSL or with OpenSSL.
- +Configuring a constraint without proper TLS library support causes
- .Xr ntpd 8
- to log a warning message on startup.
- .Bl -tag -width Ds
- @@ -292,7 +292,6 @@ adaption to system policy and flexibilit
- keywords may be specified only once within the configuration file. The
- following values can be changed from the highlighted defaults:
- .Bl -tag -width Ds
- -
- .It Ic auto_replies Ar number
- During OpenNTPD initialization, all NTP peers get automatic time offset value,
- if pre-conditions for automatic interval adjustment are being met. The
- @@ -365,6 +364,39 @@ to
- 4
- .El
- .Ed
- +.It Xo Ic constraint_ca Ar path-to-file
- +.Xc
- +PEM-formatted certificate bundle file for constraint HTTPS connections.
- +.Bd -literal -offset indent
- +.Bl -tag -width "Default:" -compact
- +.It Default:
- +/etc/ssl/cert.pem
- +.El
- +.Ed
- +.It Ic constraint_ca_validation Ar true | false
- +Whether to validate constraint HTTPS server certificate.
- +.Bd -literal -offset indent
- +.Bl -tag -width "Default:" -compact
- +.It Default:
- +true
- +.El
- +.Ed
- +.It Ic constraint_engine Ar libressl | openssl
- +Use either LibreSSL (
- +.Ic libressl
- +) or OpenSSL (
- +.Ic openssl
- +) for constraint HTTPS server
- +connections. To support chosen TLS engine,
- +.Xr ntpd 8
- +must be compiled and run with proper libraries installed
- +on the system. Only LibreSSL and OpenSSL are supported.
- +.Bd -literal -offset indent
- +.Bl -tag -width "Default:" -compact
- +.It Default:
- +libressl (defaults to openssl if only openssl found during compile time)
- +.El
- +.Ed
- .It Ic constraint_error_margin Ar number
- Accepted number of errors during constraint process. If error count exceeds
- this value multiplied by calculated peer count, constraint connection will
- @@ -385,7 +417,7 @@ margin value will be ignored.
- 120
- .El
- .Ed
- -.It Ic constraint_maxheaderlength Ar length
- +.It Ic constraint_max_headerlength Ar length
- Maximum allowed HTTP header length of constraint HTTPS server reply to
- be fetched in bytes. If the value is exceeded during processing,
- nothing is returned and constraint check fails.
- --- a/src/Makefile.am 2020-07-31 23:00:40.000000000 +0300
- +++ b/src/Makefile.am 2020-08-01 19:24:02.713594734 +0300
- @@ -34,11 +34,17 @@ ntpd_LDADD += $(top_builddir)/compat/lib
-
- ntpd_SOURCES = client.c
- ntpd_SOURCES += config.c
- -if HAVE_LIBTLS
- +if HAVE_SSL
- ntpd_SOURCES += constraint.c
- else
- ntpd_SOURCES += constraint-disabled.c
- endif
- +if HAVE_LIBRESSL
- +ntpd_SOURCES += constraint-libressl.c
- +endif
- +if HAVE_OPENSSL
- +ntpd_SOURCES += constraint-openssl.c
- +endif
- ntpd_SOURCES += control.c
- ntpd_SOURCES += log.c
- ntpd_SOURCES += log.h
- --- a/ChangeLog 2020-07-31 23:00:40.000000000 +0300
- +++ b/ChangeLog 2020-08-02 14:01:47.093664999 +0300
- @@ -30,7 +30,13 @@
- * Prevent the case of multiple ntpds running at once by checking presence
- of the local control socket.
-
- - * TLS certificates are now searched in TLS_CA_CERT_FILE.
- + * Many previously hardcoded values are now configurable via conf file.
- +
- + * Implemented OpenSSL support. Either LibreSSL or OpenSSL can be used.
- +
- + * Improved log entries interpretation.
- +
- + * Updated manual.
-
- The libtls library, as shipped with LibreSSL 3.1.0 or later, is
- required to use the HTTPS constraint feature, though it is not
|