Browse Source

[multiple] Y2038 32-bit signed time_t mitigations

Most OS platforms have already provided solutions to
Y2038 32-bit signed time_t 5 - 10 years ago (or more!)
Notable exceptions are Linux i686 and FreeBSD i386.

Since 32-bit systems tend to be embedded systems,
and since many distros take years to pick up new software,
this commit aims to provide Y2038 mitigations for lighttpd
running on 32-bit systems with Y2038-unsafe 32-bit signed time_t

* Y2038: lighttpd 1.4.60 and later report Y2038 safety
  $ lighttpd -V
    + Y2038 support                                    # Y2038-SAFE
  $ lighttpd -V
    - Y2038 support (unsafe 32-bit signed time_t)      # Y2038-UNSAFE

* Y2038: general platform info
  * Y2038-SAFE: lighttpd 64-bit builds on platforms using 64-bit time_t
      - all major 64-bit platforms (known to this author) use 64-bit time_t
  * Y2038-SAFE: lighttpd 32-bit builds on platforms using 64-bit time_t
      - Linux x32 ABI (different from i686)
      - FreeBSD all 32-bit and 64-bit architectures *except* 32-bit i386
      - NetBSD 6.0 (released Oct 2012) all 32-bit and 64-bit architectures
      - OpenBSD 5.5 (released May 2014) all 32-bit and 64-bit architectures
      - Microsoft Windows XP and Visual Studio 2005 (? unsure ?)
        Another reference suggests Visual Studio 2015 defaults to 64-bit time_t
      - MacOS 10.15 Catalina (released 2019) drops support for 32-bit apps
  * Y2038-SAFE: lighttpd 32-bit builds on platforms using 32-bit unsigned time_t
      - e.g. OpenVMS (unknown if lighttpd builds on this platform)
  * Y2038-UNSAFE: lighttpd 32-bit builds on platforms using 32-bit signed time_t
      - Linux 32-bit (including i686)
          - glibc 32-bit library support not yet available for 64-bit time_t
              - https://sourceware.org/glibc/wiki/Y2038ProofnessDesign
              - Linux kernel 5.6 on 32-bit platforms does support 64-bit time_t
                https://itsubuntu.com/linux-kernel-5-6-to-fix-the-year-2038-issue-unix-y2k/
              - https://www.gnu.org/software/libc/manual/html_node/64_002dbit-time-symbol-handling.html
                "Note: at this point, 64-bit time support in dual-time
                 configurations is work-in-progress, so for these
                 configurations, the public API only makes the 32-bit time
                 support available. In a later change, the public API will
                 allow user code to choose the time size for a given
                 compilation unit."
              - compiling with -D_TIME_BITS=64 currently has no effect
          - glibc recent (Jul 2021) mailing list discussion
              - https://public-inbox.org/bug-gnulib/878s2ozq70.fsf@oldenburg.str.redhat.com/T/
      - FreeBSD i386
      - DragonFlyBSD 32-bit

* Y2038 mitigations attempted on Y2038-UNSAFE platforms (32-bit signed time_t)
  * lighttpd prefers system monotonic clock instead of realtime clock
    in places where realtime clock is not required
  * lighttpd treats negative time_t values as after 19 Jan 2038 03:14:07 GMT
  * (lighttpd presumes that lighttpd will not encounter dates before 1970
    during normal operation.)
  * lighttpd casts struct stat st.st_mtime (and st.st_*time) through uint64_t
    to convert negative timestamps for comparisions with 64-bit timestamps
    (treating negative timestamp values as after 19 Jan 2038 03:14:07 GMT)
  * lighttpd provides unix_time64_t (int64_t) and
  * lighttpd provides struct unix_timespec64 (unix_timespec64_t)
    (struct timespec equivalent using unix_time64_t tv_sec member)
  * lighttpd provides gmtime64_r() and localtime64_r() wrappers
    for platforms 32-bit platforms using 32-bit time_t and
    lighttpd temporarily shifts the year in order to use
    gmtime_r() and localtime_r() (or gmtime() and localtime())
    from standard libraries, before readjusting year and passing
    struct tm to formatting functions such as strftime()
  * lighttpd provides TIME64_CAST() macro to cast signed 32-bit time_t to
    unsigned 32-bit and then to unix_time64_t

* Note: while lighttpd tries handle times past 19 Jan 2038 03:14:07 GMT
  on 32-bit platforms using 32-bit signed time_t, underlying libraries and
  underlying filesystems might not behave properly after 32-bit signed time_t
  overflows (19 Jan 2038 03:14:08 GMT).  If a given 32-bit OS does not work
  properly using negative time_t values, then lighttpd likely will not work
  properly on that system.

* Other references and blogs
  - https://en.wikipedia.org/wiki/Year_2038_problem
  - https://en.wikipedia.org/wiki/Time_formatting_and_storage_bugs
  - http://www.lieberbiber.de/2017/03/14/a-look-at-the-year-20362038-problems-and-time-proofness-in-various-systems/
master
Glenn Strauss 3 months ago
parent
commit
309c1693ac
  1. 14
      src/base.h
  2. 4
      src/connections.c
  3. 2
      src/connections.h
  4. 10
      src/fdevent.c
  5. 4
      src/fdevent.h
  6. 43
      src/first.h
  7. 6
      src/gw_backend.c
  8. 6
      src/gw_backend.h
  9. 4
      src/h2.c
  10. 4
      src/h2.h
  11. 13
      src/http-header-glue.c
  12. 14
      src/http_date.c
  13. 4
      src/http_date.h
  14. 75
      src/log.c
  15. 9
      src/log.h
  16. 22
      src/mod_accesslog.c
  17. 24
      src/mod_auth.c
  18. 5
      src/mod_cml_lua.c
  19. 10
      src/mod_dirlisting.c
  20. 6
      src/mod_expire.c
  21. 33
      src/mod_gnutls.c
  22. 19
      src/mod_mbedtls.c
  23. 18
      src/mod_nss.c
  24. 69
      src/mod_openssl.c
  25. 55
      src/mod_secdownload.c
  26. 19
      src/mod_ssi.c
  27. 10
      src/mod_status.c
  28. 26
      src/mod_trigger_b4_dl.c
  29. 6
      src/mod_userdir.c
  30. 8
      src/mod_vhostdb.c
  31. 2
      src/mod_webdav.c
  32. 59
      src/mod_wolfssl.c
  33. 6
      src/mod_wstunnel.c
  34. 2
      src/request.h
  35. 4
      src/response.c
  36. 4
      src/response.h
  37. 22
      src/server.c
  38. 10
      src/stat_cache.c
  39. 2
      src/stat_cache.h
  40. 105
      src/sys-time.h

14
src/base.h

@ -53,11 +53,11 @@ struct connection {
const struct server_socket *srv_socket; /* reference to the server-socket */
/* timestamps */
time_t read_idle_ts;
time_t close_timeout_ts;
time_t write_request_ts;
unix_time64_t read_idle_ts;
unix_time64_t close_timeout_ts;
unix_time64_t write_request_ts;
unix_time64_t connection_start;
time_t connection_start;
uint32_t request_count; /* number of requests handled in this connection */
int keep_alive_idle; /* remember max_keep_alive_idle from config */
};
@ -172,7 +172,7 @@ struct server {
log_error_st *errh;
time_t loadts;
unix_time64_t loadts;
double loadavg[3];
/* members used at start-up or rarely used */
@ -184,8 +184,8 @@ struct server {
server_socket_array srv_sockets_inherited;
buffer_plugin plugins;
time_t startup_ts;
time_t graceful_expire_ts;
unix_time64_t startup_ts;
unix_time64_t graceful_expire_ts;
uid_t uid;
gid_t gid;

4
src/connections.c

@ -1423,7 +1423,7 @@ connection_state_machine (connection * const con)
}
static void connection_check_timeout (connection * const con, const time_t cur_ts) {
static void connection_check_timeout (connection * const con, const unix_time64_t cur_ts) {
const int waitevents = fdevent_fdnode_interest(con->fdn);
int changed = 0;
int t_diff;
@ -1580,7 +1580,7 @@ static void connection_check_timeout (connection * const con, const time_t cur_t
}
}
void connection_periodic_maint (server * const srv, const time_t cur_ts) {
void connection_periodic_maint (server * const srv, const unix_time64_t cur_ts) {
/* check all connections for timeouts */
connections * const conns = &srv->conns;
for (size_t ndx = 0; ndx < conns->used; ++ndx) {

2
src/connections.h

@ -12,7 +12,7 @@ void connections_free(server *srv);
__attribute_cold__
void connection_graceful_shutdown_maint (server *srv);
void connection_periodic_maint (server *srv, time_t cur_ts);
void connection_periodic_maint (server *srv, unix_time64_t cur_ts);
int connection_send_1xx (request_st *r, connection *con);

10
src/fdevent.c

@ -828,7 +828,7 @@ typedef struct fdevent_cmd_pipe {
pid_t pid;
int fds[2];
const char *cmd;
time_t start;
unix_time64_t start;
} fdevent_cmd_pipe;
typedef struct fdevent_cmd_pipes {
@ -868,7 +868,7 @@ static pid_t fdevent_open_logger_pipe_spawn(const char *logger, int rfd) {
}
static void fdevent_restart_logger_pipe(fdevent_cmd_pipe *fcp, time_t ts) {
static void fdevent_restart_logger_pipe(fdevent_cmd_pipe *fcp, unix_time64_t ts) {
if (fcp->pid > 0) return; /* assert */
if (fcp->start + 5 < ts) { /* limit restart to once every 5 sec */
/* restart child process using existing pipe fds */
@ -878,7 +878,7 @@ static void fdevent_restart_logger_pipe(fdevent_cmd_pipe *fcp, time_t ts) {
}
void fdevent_restart_logger_pipes(time_t ts) {
void fdevent_restart_logger_pipes(unix_time64_t ts) {
for (uint32_t i = 0; i < cmd_pipes.used; ++i) {
fdevent_cmd_pipe * const fcp = cmd_pipes.ptr+i;
if (fcp->pid > 0) continue;
@ -887,7 +887,7 @@ void fdevent_restart_logger_pipes(time_t ts) {
}
int fdevent_waitpid_logger_pipe_pid(pid_t pid, time_t ts) {
int fdevent_waitpid_logger_pipe_pid(pid_t pid, unix_time64_t ts) {
for (uint32_t i = 0; i < cmd_pipes.used; ++i) {
fdevent_cmd_pipe * const fcp = cmd_pipes.ptr+i;
if (pid != fcp->pid) continue;
@ -911,7 +911,7 @@ int fdevent_reaped_logger_pipe(pid_t pid) {
for (uint32_t i = 0; i < cmd_pipes.used; ++i) {
fdevent_cmd_pipe *fcp = cmd_pipes.ptr+i;
if (fcp->pid == pid) {
time_t ts = log_monotonic_secs;
unix_time64_t ts = log_monotonic_secs;
if (fcp->start + 5 < ts) { /* limit restart to once every 5 sec */
fcp->start = ts;
fcp->pid = fdevent_open_logger_pipe_spawn(fcp->cmd,fcp->fds[0]);

4
src/fdevent.h

@ -104,8 +104,8 @@ int fdevent_waitpid_intr(pid_t pid, int *status);
int fdevent_open_logger(const char *logger);
int fdevent_cycle_logger(const char *logger, int *curfd);
int fdevent_reaped_logger_pipe(pid_t pid);
int fdevent_waitpid_logger_pipe_pid(pid_t pid, time_t ts);
void fdevent_restart_logger_pipes(time_t ts);
int fdevent_waitpid_logger_pipe_pid(pid_t pid, unix_time64_t ts);
void fdevent_restart_logger_pipes(unix_time64_t ts);
void fdevent_close_logger_pipes(void);
void fdevent_breakagelog_logger_pipe(int fd);
void fdevent_clr_logger_pipe_pids(void);

43
src/first.h

@ -53,6 +53,49 @@
#endif
/* TODO: would be more accurate to create build-system test for sizeof(time_t)*/
#ifndef HAS_TIME_BITS64
#if defined(_LP64) || defined(__LP64__) || defined(_WIN64)
#define HAS_TIME_BITS64 1
#elif defined(__TIMESIZE)
#if __TIMESIZE == 64
#define HAS_TIME_BITS64 1
#else
#define HAS_TIME_BITS64 0
#endif
#elif defined(_WIN32)
#ifndef _USE_32BIT_TIME_T
#define HAS_TIME_BITS64 1
#else
#define HAS_TIME_BITS64 0
#endif
#elif defined(_ILP32) \
&& !defined(__NetBSD__) && !defined(__OpenBSD__) \
&& (!defined(__FreeBSD__) || !defined(__i386__)) \
&& !(defined(__APPLE__) && defined(__MACH__))
#define HAS_TIME_BITS64 0
#else
#define HAS_TIME_BITS64 1
#endif
#endif
/* non-standard types created for lighttpd for Y2038 problem
* reference: https://en.wikipedia.org/wiki/Year_2038_problem */
#if HAS_TIME_BITS64
typedef time_t unix_time64_t;
typedef struct timespec unix_timespec64_t;
#define TIME64_CAST(t) (t)
#else /* !HAS_TIME_BITS64 */
typedef int64_t unix_time64_t;
struct unix_timespec64 {
unix_time64_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
typedef struct unix_timespec64 unix_timespec64_t;
#define TIME64_CAST(t) ((unix_time64_t)(uint32_t)(t))
#endif /* !HAS_TIME_BITS64 */
#define UNUSED(x) ( (void)(x) )

6
src/gw_backend.c

@ -246,7 +246,7 @@ static void gw_proc_connect_success(gw_host *host, gw_proc *proc, int debug, req
__attribute_cold__
static void gw_proc_connect_error(request_st * const r, gw_host *host, gw_proc *proc, pid_t pid, int errnum, int debug) {
const time_t cur_ts = log_monotonic_secs;
const unix_time64_t cur_ts = log_monotonic_secs;
log_error_st * const errh = r->conf.errh;
log_perror(errh, __FILE__, __LINE__, /*(caller should set errno = errnum)*/
"establishing connection failed: socket: %s", proc->connection_name->ptr);
@ -2581,7 +2581,7 @@ static void gw_handle_trigger_host(gw_host * const host, log_error_st * const er
/* check each child proc to detect if proc exited */
gw_proc *proc;
time_t idle_timestamp;
unix_time64_t idle_timestamp;
int overload = 1;
for (proc = host->first; proc; proc = proc->next) {
@ -2729,7 +2729,7 @@ handler_t gw_handle_waitpid_cb(server *srv, void *p_d, pid_t pid, int status) {
* or global scope (for convenience))
* (unable to use p->defaults.debug since gw_plugin_config
* might be part of a larger plugin_config) */
const time_t cur_ts = log_monotonic_secs;
const unix_time64_t cur_ts = log_monotonic_secs;
gw_exts *exts = conf->exts;
for (uint32_t j = 0; j < exts->used; ++j) {
gw_extension *ex = exts->exts+j;

6
src/gw_backend.h

@ -26,7 +26,7 @@ typedef struct gw_proc {
PROC_STATE_KILLED /* killed (signal sent to proc) */
} state;
uint32_t load; /* number of requests waiting on this process */
time_t last_used; /* see idle_timeout */
unix_time64_t last_used; /* see idle_timeout */
int *stats_load;
int *stats_connected;
pid_t pid; /* PID of the spawned process (0 if not spawned locally) */
@ -35,7 +35,7 @@ typedef struct gw_proc {
socklen_t saddrlen;
struct sockaddr *saddr;
time_t disabled_until; /* proc disabled until given time */
unix_time64_t disabled_until; /* proc disabled until given time */
struct gw_proc *prev; /* see first */
/* either tcp:<host>:<port> or unix:<socket> for debugging purposes */
@ -301,7 +301,7 @@ typedef struct gw_handler_ctx {
unsigned short gw_mode; /* mode: GW_AUTHORIZER or GW_RESPONDER */
gw_connection_state_t state;
time_t state_timestamp;
/*unix_time64_t state_timestamp;*//*(unused)*/
chunkqueue *rb; /* read queue */
off_t wb_reqlen;

4
src/h2.c

@ -2005,7 +2005,7 @@ h2_send_headers (request_st * const r, connection * const con)
if (!light_btst(r->resp_htags, HTTP_HEADER_DATE)) {
/* HTTP/1.1 and later requires a Date: header */
/* "date: " 6-chars + 30-chars for "%a, %d %b %Y %T GMT" + '\0' */
static time_t tlast = 0;
static unix_time64_t tlast = 0;
static char tstr[36] = "date: ";
memset(&lsx, 0, sizeof(lsxpack_header_t));
@ -2017,7 +2017,7 @@ h2_send_headers (request_st * const r, connection * const con)
lsx.hpack_index = LSHPACK_HDR_DATE;
/* cache the generated timestamp */
const time_t cur_ts = log_epoch_secs;
const unix_time64_t cur_ts = log_epoch_secs;
if (__builtin_expect ( (tlast != cur_ts), 0))
http_date_time_to_str(tstr+6, sizeof(tstr)-6, (tlast = cur_ts));

4
src/h2.h

@ -75,7 +75,7 @@ struct h2con {
uint32_t h2_cid;
uint32_t h2_sid;
int32_t sent_goaway;
time_t sent_settings;
unix_time64_t sent_settings;
uint32_t s_header_table_size; /* SETTINGS_HEADER_TABLE_SIZE */
uint32_t s_enable_push; /* SETTINGS_ENABLE_PUSH */
uint32_t s_max_concurrent_streams; /* SETTINGS_MAX_CONCURRENT_STREAMS */
@ -84,7 +84,7 @@ struct h2con {
uint32_t s_max_header_list_size; /* SETTINGS_MAX_HEADER_LIST_SIZE */
struct lshpack_dec decoder;
struct lshpack_enc encoder;
time_t half_closed_ts;
unix_time64_t half_closed_ts;
};
void h2_send_goaway (connection *con, request_h2error_t e);

13
src/http-header-glue.c

@ -134,7 +134,7 @@ int http_response_redirect_to_directory(request_st * const r, int status) {
#define MTIME_CACHE_MAX 16
struct mtime_cache_type {
time_t mtime; /* key */
unix_time64_t mtime; /* key */
buffer str; /* buffer for string representation */
};
static struct mtime_cache_type mtime_cache[MTIME_CACHE_MAX];
@ -143,14 +143,17 @@ static char mtime_cache_str[MTIME_CACHE_MAX][HTTP_DATE_SZ];
void strftime_cache_reset(void) {
for (int i = 0; i < MTIME_CACHE_MAX; ++i) {
mtime_cache[i].mtime = (time_t)-1;
mtime_cache[i].mtime = -1;
mtime_cache[i].str.ptr = mtime_cache_str[i];
mtime_cache[i].str.used = sizeof(mtime_cache_str[0]);
mtime_cache[i].str.size = sizeof(mtime_cache_str[0]);
}
}
static const buffer * strftime_cache_get(const time_t last_mod) {
static const buffer * strftime_cache_get(const unix_time64_t last_mod) {
/*(note: not bothering to convert *here* if last_mod < 0 (for cache key);
* last_mod < 0 handled in http_date_time_to_str() call to gmtime64_r())*/
static int mtime_cache_idx;
for (int j = 0; j < MTIME_CACHE_MAX; ++j) {
@ -168,7 +171,7 @@ static const buffer * strftime_cache_get(const time_t last_mod) {
}
const buffer * http_response_set_last_modified(request_st * const r, const time_t lmtime) {
const buffer * http_response_set_last_modified(request_st * const r, const unix_time64_t lmtime) {
buffer * const vb =
http_header_response_set_ptr(r, HTTP_HEADER_LAST_MODIFIED,
CONST_STR_LEN("Last-Modified"));
@ -177,7 +180,7 @@ const buffer * http_response_set_last_modified(request_st * const r, const time_
}
int http_response_handle_cachable(request_st * const r, const buffer * const lmod, const time_t lmtime) {
int http_response_handle_cachable(request_st * const r, const buffer * const lmod, const unix_time64_t lmtime) {
if (!(r->rqst_htags
& (light_bshift(HTTP_HEADER_IF_NONE_MATCH)
|light_bshift(HTTP_HEADER_IF_MODIFIED_SINCE)))) {

14
src/http_date.c

@ -47,14 +47,14 @@ http_date_parse_RFC_850 (const char *s, struct tm * const tm)
* than 50 years in the future as representing the most recent year in
* the past that had the same last two digits.
*/
static time_t tm_year_last_check;
static unix_time64_t tm_year_last_check;
static int tm_year_cur;
static int tm_year_base;
/* (log_epoch_secs is a global variable, maintained elsewhere) */
/* (optimization: check for year change no more than once per min) */
if (log_epoch_secs >= tm_year_last_check + 60) {
struct tm tm_cur;
if (NULL != gmtime_r(&log_epoch_secs, &tm_cur)) {
if (NULL != gmtime64_r(&log_epoch_secs, &tm_cur)) {
tm_year_last_check = log_epoch_secs;
if (tm_cur.tm_year != tm_year_cur) {
tm_year_cur = tm_cur.tm_year;
@ -265,12 +265,12 @@ http_date_str_to_tm (const char * const s, const uint32_t len,
uint32_t
http_date_time_to_str (char * const s, const size_t sz, const time_t t)
http_date_time_to_str (char * const s, const size_t sz, const unix_time64_t t)
{
/*('max' is expected to be >= 30 (IMF-fixdate is 29 chars + '\0'))*/
struct tm tm;
const char fmt[] = "%a, %d %b %Y %T GMT"; /*IMF-fixdate fmt*/
return (__builtin_expect( (NULL != gmtime_r(&t, &tm)), 1))
return (__builtin_expect( (NULL != gmtime64_r(&t, &tm)), 1))
? (uint32_t)strftime(s, sz, fmt, &tm)
: 0;
}
@ -278,13 +278,17 @@ http_date_time_to_str (char * const s, const size_t sz, const time_t t)
int
http_date_if_modified_since (const char * const ifmod, const uint32_t ifmodlen,
const time_t lmtime)
const unix_time64_t lmtime)
{
struct tm ifmodtm;
if (NULL == http_date_str_to_tm(ifmod, ifmodlen, &ifmodtm))
return 1; /* date parse error */
const time_t ifmtime = timegm(&ifmodtm);
#if HAS_TIME_BITS64
return (lmtime > ifmtime);
#else
return (TIME64_CAST(lmtime) > TIME64_CAST(ifmtime) || ifmtime==(time_t)-1);
#endif
/* returns 0 if not modified since,
* returns 1 if modified since or date parse error */
}

4
src/http_date.h

@ -18,9 +18,9 @@ extern "C" {
#define HTTP_DATE_SZ 30 /* (IMF-fixdate is 29 chars + '\0') */
uint32_t http_date_time_to_str (char *s, size_t sz, time_t t);
uint32_t http_date_time_to_str (char *s, size_t sz, unix_time64_t t);
int http_date_if_modified_since (const char *ifmod, uint32_t ifmodlen, time_t lmtime);
int http_date_if_modified_since (const char *ifmod, uint32_t ifmodlen, unix_time64_t lmtime);
/*(convenience macro to append IMF-fixdate to (buffer *))*/
#define http_date_time_append(b, t) \

75
src/log.c

@ -21,29 +21,58 @@
# include <syslog.h>
#endif
time_t log_epoch_secs = 0;
time_t log_monotonic_secs = 0;
int log_clock_gettime_realtime (struct timespec *ts) {
#ifdef HAVE_CLOCK_GETTIME
return clock_gettime(CLOCK_REALTIME, ts);
#else
/* Mac OSX does not provide clock_gettime()
* e.g. defined(__APPLE__) && defined(__MACH__) */
struct timeval tv;
gettimeofday(&tv, NULL);
ts->tv_sec = tv.tv_sec;
ts->tv_nsec = tv.tv_usec * 1000;
return 0;
#endif
unix_time64_t log_epoch_secs = 0;
unix_time64_t log_monotonic_secs = 0;
int log_clock_gettime_realtime (unix_timespec64_t *ts) {
#ifdef HAVE_CLOCK_GETTIME
#if HAS_TIME_BITS64
return clock_gettime(CLOCK_REALTIME, ts);
#else
struct timespec ts32;
int rc = clock_gettime(CLOCK_REALTIME, &ts32);
if (0 == rc) {
/*(treat negative 32-bit tv.tv_sec as unsigned)*/
ts->tv_sec = TIME64_CAST(ts32.tv_sec);
ts->tv_nsec = ts32.tv_nsec;
}
return rc;
#endif
#else
/* Mac OSX before 10.12 Sierra does not provide clock_gettime()
* e.g. defined(__APPLE__) && defined(__MACH__)
* && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 101200 */
struct timeval tv;
gettimeofday(&tv, NULL);
#if HAS_TIME_BITS64
ts->tv_sec = tv.tv_sec;
#else /*(treat negative 32-bit tv.tv_sec as unsigned)*/
ts->tv_sec = TIME64_CAST(tv.tv_sec);
#endif
ts->tv_nsec = tv.tv_usec * 1000;
return 0;
#endif
}
int log_clock_gettime_monotonic (struct timespec *ts) {
#ifdef HAVE_CLOCK_GETTIME
return clock_gettime(CLOCK_MONOTONIC, ts);
#else
return log_clock_gettime_realtime(ts); /*(fallback)*/
#endif
int log_clock_gettime_monotonic (unix_timespec64_t *ts) {
#ifdef HAVE_CLOCK_GETTIME
#if HAS_TIME_BITS64
return clock_gettime(CLOCK_MONOTONIC, ts);
#else
struct timespec ts32;
int rc = clock_gettime(CLOCK_MONOTONIC, &ts32);
if (0 == rc) {
/*(treat negative 32-bit tv.tv_sec as unsigned)*/
/*(negative 32-bit should not happen on monotonic clock
* unless system running continously for > 68 years)*/
ts->tv_sec = TIME64_CAST(ts32.tv_sec);
ts->tv_nsec = ts32.tv_nsec;
}
return rc;
#endif
#else
return log_clock_gettime_realtime(ts); /*(fallback)*/
#endif
}
/* retry write on EINTR or when not all data was written */
@ -64,7 +93,7 @@ ssize_t write_all(int fd, const void * const buf, size_t count) {
}
static int log_buffer_prepare(const log_error_st *errh, const char *filename, unsigned int line, buffer *b) {
static time_t tlast;
static unix_time64_t tlast;
static uint32_t tlen;
static char tstr[24]; /* 20 "%F %T" incl '\0' +3 ": (" */
switch(errh->errorlog_mode) {
@ -78,7 +107,7 @@ static int log_buffer_prepare(const log_error_st *errh, const char *filename, un
tlast = log_epoch_secs;
tlen = (uint32_t)
strftime(tstr, sizeof(tstr), "%F %T",
localtime_r(&tlast, &tm));
localtime64_r(&tlast, &tm));
tstr[ tlen] = ':';
tstr[++tlen] = ' ';
tstr[++tlen] = '(';

9
src/log.h

@ -5,12 +5,11 @@
#include "base_decls.h"
#include "buffer.h"
extern time_t log_epoch_secs;
extern time_t log_monotonic_secs;
extern unix_time64_t log_epoch_secs;
extern unix_time64_t log_monotonic_secs;
struct timespec; /* declaration */
int log_clock_gettime_realtime (struct timespec *ts);
int log_clock_gettime_monotonic (struct timespec *ts);
int log_clock_gettime_realtime (unix_timespec64_t *ts);
int log_clock_gettime_monotonic (unix_timespec64_t *ts);
ssize_t write_all(int fd, const void* buf, size_t count);

22
src/mod_accesslog.c

@ -137,7 +137,7 @@ typedef struct {
} format_field;
typedef struct {
time_t last_generated_accesslog_ts;
unix_time64_t last_generated_accesslog_ts;
buffer ts_accesslog_str;
#if defined(__STDC_VERSION__) && __STDC_VERSION__-0 >= 199901L /* C99 */
format_field ptr[]; /* C99 VLA */
@ -793,7 +793,7 @@ SIGHUP_FUNC(log_access_cycle) {
static int log_access_record (const request_st * const r, buffer * const b, format_fields * const parsed_format) {
const connection * const con = r->con;
const buffer *vb;
struct timespec ts = { 0, 0 };
unix_timespec64_t ts = { 0, 0 };
int flush = 0;
for (const format_field *f = parsed_format->ptr; f->type != FIELD_UNSET; ++f) {
@ -807,17 +807,17 @@ static int log_access_record (const request_st * const r, buffer * const b, form
if (f->opt & ~(FORMAT_FLAG_TIME_BEGIN|FORMAT_FLAG_TIME_END)) {
if (f->opt & FORMAT_FLAG_TIME_SEC) {
time_t t = (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) ? log_epoch_secs : r->start_hp.tv_sec;
unix_time64_t t = (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) ? log_epoch_secs : r->start_hp.tv_sec;
buffer_append_int(b, (intmax_t)t);
} else if (f->opt & (FORMAT_FLAG_TIME_MSEC|FORMAT_FLAG_TIME_USEC|FORMAT_FLAG_TIME_NSEC)) {
off_t t; /*(expected to be 64-bit since large file support enabled)*/
unix_time64_t t;
long ns;
if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) {
if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts);
t = (off_t)ts.tv_sec;
t = ts.tv_sec;
ns = ts.tv_nsec;
} else {
t = (off_t)r->start_hp.tv_sec;
t = r->start_hp.tv_sec;
ns = r->start_hp.tv_nsec;
}
if (f->opt & FORMAT_FLAG_TIME_MSEC) {
@ -858,11 +858,11 @@ static int log_access_record (const request_st * const r, buffer * const b, form
} else {
buffer * const ts_accesslog_str = &parsed_format->ts_accesslog_str;
/* cache the generated timestamp (only if ! FORMAT_FLAG_TIME_BEGIN) */
time_t t;
unix_time64_t t;
struct tm tm;
if (!(f->opt & FORMAT_FLAG_TIME_BEGIN)) {
const time_t cur_ts = log_epoch_secs;
const unix_time64_t cur_ts = log_epoch_secs;
if (parsed_format->last_generated_accesslog_ts == cur_ts) {
buffer_append_string_buffer(b, ts_accesslog_str);
break;
@ -880,11 +880,11 @@ static int log_access_record (const request_st * const r, buffer * const b, form
#if defined(HAVE_STRUCT_TM_GMTOFF)
buffer_append_strftime(ts_accesslog_str,
fmt ? fmt : "[%d/%b/%Y:%T %z]",
localtime_r(&t, &tm));
localtime64_r(&t, &tm));
#else /* HAVE_STRUCT_TM_GMTOFF */
buffer_append_strftime(ts_accesslog_str,
fmt ? fmt : "[%d/%b/%Y:%T +0000]",
gmtime_r(&t, &tm));
gmtime64_r(&t, &tm));
#endif /* HAVE_STRUCT_TM_GMTOFF */
buffer_append_string_buffer(b, ts_accesslog_str);
}
@ -894,7 +894,7 @@ static int log_access_record (const request_st * const r, buffer * const b, form
if (f->opt & FORMAT_FLAG_TIME_SEC) {
buffer_append_int(b, log_epoch_secs - r->start_hp.tv_sec);
} else {
const struct timespec * const bs = &r->start_hp;
const unix_timespec64_t * const bs = &r->start_hp;
off_t tdiff; /*(expected to be 64-bit since large file support enabled)*/
if (0 == ts.tv_sec) log_clock_gettime_realtime(&ts);
tdiff = (off_t)(ts.tv_sec - bs->tv_sec)*1000000000 + (ts.tv_nsec - bs->tv_nsec);

24
src/mod_auth.c

@ -45,7 +45,7 @@ typedef struct {
typedef struct {
const struct http_auth_require_t *require;
time_t ctime;
unix_time64_t ctime;
int dalgo;
uint32_t dlen;
uint32_t ulen;
@ -144,7 +144,7 @@ http_auth_cache_insert (splay_tree ** const sptree, const int ndx, void * const
/* walk though cache, collect expired ids, and remove them in a second loop */
static void
mod_auth_tag_old_entries (splay_tree * const t, int * const keys, int * const ndx, const time_t max_age, const time_t cur_ts)
mod_auth_tag_old_entries (splay_tree * const t, int * const keys, int * const ndx, const time_t max_age, const unix_time64_t cur_ts)
{
if (*ndx == 8192) return; /*(must match num array entries in keys[])*/
if (t->left)
@ -160,7 +160,7 @@ mod_auth_tag_old_entries (splay_tree * const t, int * const keys, int * const nd
__attribute_noinline__
static void
mod_auth_periodic_cleanup(splay_tree **sptree_ptr, const time_t max_age, const time_t cur_ts)
mod_auth_periodic_cleanup(splay_tree **sptree_ptr, const time_t max_age, const unix_time64_t cur_ts)
{
splay_tree *sptree = *sptree_ptr;
int max_ndx, i;
@ -184,7 +184,7 @@ mod_auth_periodic_cleanup(splay_tree **sptree_ptr, const time_t max_age, const t
TRIGGER_FUNC(mod_auth_periodic)
{
const plugin_data * const p = p_d;
const time_t cur_ts = log_monotonic_secs;
const unix_time64_t cur_ts = log_monotonic_secs;
if (cur_ts & 0x7) return HANDLER_GO_ON; /*(continue once each 8 sec)*/
UNUSED(srv);
@ -867,7 +867,7 @@ enum http_auth_digest_params_e {
typedef struct http_auth_digest_params_t {
const char *ptr[http_auth_digest_params_sz];
uint16_t len[http_auth_digest_params_sz];
time_t send_nextnonce_ts;
unix_time64_t send_nextnonce_ts;
unsigned char rdigest[MD_DIGEST_LENGTH_MAX]; /*(last member)*/
} http_auth_digest_params_t;
@ -964,7 +964,7 @@ mod_auth_digest_mutate (http_auth_info_t * const ai, const http_auth_digest_para
static void
mod_auth_append_nonce (buffer *b, time_t cur_ts, const struct http_auth_require_t *require, int dalgo, int *rndptr)
mod_auth_append_nonce (buffer *b, unix_time64_t cur_ts, const struct http_auth_require_t *require, int dalgo, int *rndptr)
{
buffer_append_uint_hex(b, (uintmax_t)cur_ts);
buffer_append_string_len(b, CONST_STR_LEN(":"));
@ -1028,7 +1028,7 @@ mod_auth_append_nonce (buffer *b, time_t cur_ts, const struct http_auth_require_
static void
mod_auth_digest_www_authenticate (buffer *b, time_t cur_ts, const struct http_auth_require_t *require, int nonce_stale)
mod_auth_digest_www_authenticate (buffer *b, unix_time64_t cur_ts, const struct http_auth_require_t *require, int nonce_stale)
{
int algos = nonce_stale ? nonce_stale : require->algorithm;
int n = 0;
@ -1093,7 +1093,7 @@ mod_auth_send_401_unauthorized_digest(request_st * const r, const struct http_au
static void
mod_auth_digest_authentication_info (buffer *b, time_t cur_ts, const struct http_auth_require_t *require, int dalgo)
mod_auth_digest_authentication_info (buffer *b, unix_time64_t cur_ts, const struct http_auth_require_t *require, int dalgo)
{
buffer_clear(b);
buffer_append_string_len(b, CONST_STR_LEN("nextnonce=\""));
@ -1336,13 +1336,13 @@ mod_auth_digest_validate_nonce (request_st * const r, const struct http_auth_req
* data value (included for unique nonces) will be exposed in the nonce
* along with the timestamp, and the additional secret will be used to
* validate that the server generated the nonce using that secret. */
time_t ts = 0;
unix_time64_t ts = 0;
const unsigned char * const nonce = (unsigned char *)dp->ptr[e_nonce];
int i;
for (i = 0; i < 8 && light_isxdigit(nonce[i]); ++i)
ts =(time_t)((uint32_t)ts << 4) | hex2int(nonce[i]);
for (i = 0; i < 16 && light_isxdigit(nonce[i]); ++i)
ts = (unix_time64_t)((uint64_t)ts << 4) | hex2int(nonce[i]);
const time_t cur_ts = log_epoch_secs;
const unix_time64_t cur_ts = log_epoch_secs;
if (nonce[i] != ':' || ts > cur_ts || cur_ts - ts > 600) { /*(10 mins)*/
/* nonce is stale; have client regenerate digest */
return mod_auth_send_401_unauthorized_digest(r, require, ai->dalgo);

5
src/mod_cml_lua.c

@ -192,7 +192,7 @@ int cache_parse_lua(request_st * const r, plugin_data * const p, const buffer *
/* up to now it is a cache-hit, check if all files exist */
int curelem;
time_t mtime = 0;
unix_time64_t mtime = 0;
if (!lua_to_c_is_table(L, "output_include")) {
log_error(r->conf.errh, __FILE__, __LINE__,
@ -261,7 +261,8 @@ int cache_parse_lua(request_st * const r, plugin_data * const p, const buffer *
}
} else {
chunkqueue_append_file_fd(&r->write_queue, b, fd, 0, st.st_size);
if (st.st_mtime > mtime) mtime = st.st_mtime;
if (mtime < TIME64_CAST(st.st_mtime))
mtime = TIME64_CAST(st.st_mtime);
}
} else {
/* not a string */

10
src/mod_dirlisting.c

@ -102,8 +102,8 @@ typedef struct {
typedef struct {
uint32_t namelen;
time_t mtime;
off_t size;
unix_time64_t mtime;
off_t size;
} dirls_entry_t;
typedef struct {
@ -1125,7 +1125,7 @@ static void http_list_directory(request_st * const r, handler_ctx * const hctx)
buffer_append_string_len(out, CONST_STR_LEN("/\">"));
buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML);
buffer_append_string_len(out, CONST_STR_LEN("</a>/</td><td class=\"m\">"));
buffer_append_strftime(out, "%Y-%b-%d %T", localtime_r(&tmp->mtime, &tm));
buffer_append_strftime(out, "%Y-%b-%d %T", localtime64_r(&tmp->mtime, &tm));
buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">- &nbsp;</td><td class=\"t\">Directory</td></tr>\n"));
if (buffer_string_space(out) < 256) {
@ -1166,7 +1166,7 @@ static void http_list_directory(request_st * const r, handler_ctx * const hctx)
buffer_append_string_len(out, CONST_STR_LEN("\">"));
buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML);
buffer_append_string_len(out, CONST_STR_LEN("</a></td><td class=\"m\">"));
buffer_append_strftime(out, "%Y-%b-%d %T", localtime_r(&tmp->mtime, &tm));
buffer_append_strftime(out, "%Y-%b-%d %T", localtime64_r(&tmp->mtime, &tm));
size_t buflen =
http_list_directory_sizefmt(sizebuf, sizeof(sizebuf), tmp->size);
struct const_iovec iov[] = {
@ -1357,7 +1357,7 @@ static handler_t mod_dirlisting_cache_check (request_st * const r, plugin_data *
stat_cache_entry * const sce = stat_cache_get_entry_open(tb, 1);
if (NULL == sce || sce->fd == -1)
return HANDLER_GO_ON;
if (sce->st.st_mtime + p->conf.cache->max_age < log_epoch_secs)
if (TIME64_CAST(sce->st.st_mtime) + p->conf.cache->max_age < log_epoch_secs)
return HANDLER_GO_ON;
/* Note: dirlist < 350 or so entries will generally trigger file

6
src/mod_expire.c

@ -247,8 +247,8 @@ SETDEFAULTS_FUNC(mod_expire_set_defaults) {
static handler_t
mod_expire_set_header (request_st * const r, const time_t * const off)
{
const time_t cur_ts = log_epoch_secs;
time_t expires = off[1];
const unix_time64_t cur_ts = log_epoch_secs;
unix_time64_t expires = off[1];
if (0 == off[0]) { /* access */
expires += cur_ts;
}
@ -256,7 +256,7 @@ mod_expire_set_header (request_st * const r, const time_t * const off)
const stat_cache_st * const st = stat_cache_path_stat(&r->physical.path);
/* can't set modification-based expire if mtime is not available */
if (NULL == st) return HANDLER_GO_ON;
expires += st->st_mtime;
expires += TIME64_CAST(st->st_mtime);
}
/* expires should be at least cur_ts */

33
src/mod_gnutls.c

@ -68,8 +68,8 @@ typedef struct {
gnutls_datum_t *ssl_pemfile_x509;
gnutls_privkey_t ssl_pemfile_pkey;
const buffer *ssl_stapling_file;
time_t ssl_stapling_loadts;
time_t ssl_stapling_nextts;
unix_time64_t ssl_stapling_loadts;
unix_time64_t ssl_stapling_nextts;
} plugin_cert;
typedef struct {
@ -273,15 +273,15 @@ mod_gnutls_datum_wipe (gnutls_datum_t * const d)
* to store keys that are not yet active
* (mirror from mod_openssl, even though not all bits are used here) */
typedef struct tlsext_ticket_key_st {
time_t active_ts; /* tickets not issued w/ key until activation timestamp */
time_t expire_ts; /* key not valid after expiration timestamp */
unix_time64_t active_ts; /* tickets not issued w/ key until activation ts*/
unix_time64_t expire_ts; /* key not valid after expiration timestamp */
unsigned char tick_key_name[TLSEXT_KEYNAME_LENGTH];
unsigned char tick_hmac_key[TLSEXT_TICK_KEY_LENGTH];
unsigned char tick_aes_key[TLSEXT_TICK_KEY_LENGTH];
} tlsext_ticket_key_t;
static tlsext_ticket_key_t session_ticket_keys[1]; /* temp store until active */
static time_t stek_rotate_ts;
static unix_time64_t stek_rotate_ts;
static gnutls_datum_t session_ticket_key;
@ -348,8 +348,8 @@ mod_gnutls_session_ticket_key_file (const char *fn)
if (0 != fdevent_load_file_bytes((char *)buf,(off_t)sizeof(buf),0,fn,NULL))
return rc;
if (buf[0] == 0) { /*(format version 0)*/
session_ticket_keys[0].active_ts = buf[1];
session_ticket_keys[0].expire_ts = buf[2];
session_ticket_keys[0].active_ts = TIME64_CAST(buf[1]);
session_ticket_keys[0].expire_ts = TIME64_CAST(buf[2]);
#ifndef __COVERITY__
memcpy(&session_ticket_keys[0].tick_key_name, buf+3, 80);
#else
@ -369,16 +369,17 @@ mod_gnutls_session_ticket_key_file (const char *fn)
static void
mod_gnutls_session_ticket_key_check (server *srv, const plugin_data *p, const time_t cur_ts)
mod_gnutls_session_ticket_key_check (server *srv, const plugin_data *p, const unix_time64_t cur_ts)
{
static time_t detect_retrograde_ts;
static unix_time64_t detect_retrograde_ts;
if (detect_retrograde_ts > cur_ts && detect_retrograde_ts - cur_ts > 28800)
stek_rotate_ts = 0;
detect_retrograde_ts = cur_ts;
if (p->ssl_stek_file) {
struct stat st;
if (0 == stat(p->ssl_stek_file, &st) && st.st_mtime > stek_rotate_ts
if (0 == stat(p->ssl_stek_file, &st)
&& TIME64_CAST(st.st_mtime) > stek_rotate_ts
&& mod_gnutls_session_ticket_key_file(p->ssl_stek_file)) {
stek_rotate_ts = cur_ts;
}
@ -923,7 +924,7 @@ mod_gnutls_expire_stapling_file (server *srv, plugin_cert *pc)
static int
mod_gnutls_reload_stapling_file (server *srv, plugin_cert *pc, const time_t cur_ts)
mod_gnutls_reload_stapling_file (server *srv, plugin_cert *pc, const unix_time64_t cur_ts)
{
#if GNUTLS_VERSION_NUMBER < 0x030603
/* load file into gnutls_ocsp_resp_t before loading into
@ -970,7 +971,7 @@ mod_gnutls_reload_stapling_file (server *srv, plugin_cert *pc, const time_t cur_
pc->ssl_stapling_loadts = cur_ts;
pc->ssl_stapling_nextts = nextupd;
if (pc->ssl_stapling_nextts == (time_t)-1) {
if (pc->ssl_stapling_nextts == -1) {
/* "Next Update" might not be provided by OCSP responder
* Use 3600 sec (1 hour) in that case. */
/* retry in 1 hour if unable to determine Next Update */
@ -985,13 +986,13 @@ mod_gnutls_reload_stapling_file (server *srv, plugin_cert *pc, const time_t cur_
static int
mod_gnutls_refresh_stapling_file (server *srv, plugin_cert *pc, const time_t cur_ts)
mod_gnutls_refresh_stapling_file (server *srv, plugin_cert *pc, const unix_time64_t cur_ts)
{
if (pc->ssl_stapling_nextts > cur_ts + 256)
return 0; /* skip check for refresh unless close to expire */
struct stat st;
if (0 != stat(pc->ssl_stapling_file->ptr, &st)
|| st.st_mtime <= pc->ssl_stapling_loadts) {
|| TIME64_CAST(st.st_mtime) <= pc->ssl_stapling_loadts) {
if (pc->ssl_stapling_nextts < cur_ts)
mod_gnutls_expire_stapling_file(srv, pc);
return 0;
@ -1001,7 +1002,7 @@ mod_gnutls_refresh_stapling_file (server *srv, plugin_cert *pc, const time_t cur
static void
mod_gnutls_refresh_stapling_files (server *srv, const plugin_data *p, const time_t cur_ts)
mod_gnutls_refresh_stapling_files (server *srv, const plugin_data *p, const unix_time64_t cur_ts)
{
/* future: might construct array of (plugin_cert *) at startup
* to avoid the need to search for them here */
@ -2979,7 +2980,7 @@ REQUEST_FUNC(mod_gnutls_handle_request_reset)
TRIGGER_FUNC(mod_gnutls_handle_trigger) {
const plugin_data * const p = p_d;
const time_t cur_ts = log_epoch_secs;
const unix_time64_t cur_ts = log_epoch_secs;
if (cur_ts & 0x3f) return HANDLER_GO_ON; /*(continue once each 64 sec)*/
mod_gnutls_session_ticket_key_check(srv, p, cur_ts);

19
src/mod_mbedtls.c

@ -262,15 +262,15 @@ static void elogf(log_error_st * const errh,
* to store keys that are not yet active
* (mirror from mod_openssl, even though not all bits are used here) */
typedef struct tlsext_ticket_key_st {
time_t active_ts; /* tickets not issued w/ key until activation timestamp */
time_t expire_ts; /* key not valid after expiration timestamp */
unix_time64_t active_ts; /* tickets not issued w/ key until activation ts*/
unix_time64_t expire_ts; /* key not valid after expiration timestamp */
unsigned char tick_key_name[TLSEXT_KEYNAME_LENGTH];
unsigned char tick_hmac_key[TLSEXT_TICK_KEY_LENGTH];
unsigned char tick_aes_key[TLSEXT_TICK_KEY_LENGTH];
} tlsext_ticket_key_t;
static tlsext_ticket_key_t session_ticket_keys[1]; /* temp store until active */
static time_t stek_rotate_ts;
static unix_time64_t stek_rotate_ts;
static int
@ -311,8 +311,8 @@ mod_mbedtls_session_ticket_key_file (const char *fn)
if (0 != fdevent_load_file_bytes((char *)buf,(off_t)sizeof(buf),0,fn,NULL))
return rc;
if (buf[0] == 0) { /*(format version 0)*/
session_ticket_keys[0].active_ts = buf[1];
session_ticket_keys[0].expire_ts = buf[2];
session_ticket_keys[0].active_ts = TIME64_CAST(buf[1]);
session_ticket_keys[0].expire_ts = TIME64_CAST(buf[2]);
#ifndef __COVERITY__
memcpy(&session_ticket_keys[0].tick_key_name, buf+3, 80);
#else
@ -332,12 +332,13 @@ mod_mbedtls_session_ticket_key_file (const char *fn)
static void
mod_mbedtls_session_ticket_key_check (plugin_data *p, const time_t cur_ts)
mod_mbedtls_session_ticket_key_check (plugin_data *p, const unix_time64_t cur_ts)
{
if (NULL == p->ssl_stek_file) return;
struct stat st;
if (0 == stat(p->ssl_stek_file, &st) && st.st_mtime > stek_rotate_ts
if (0 == stat(p->ssl_stek_file, &st)
&& TIME64_CAST(st.st_mtime) > stek_rotate_ts
&& mod_mbedtls_session_ticket_key_file(p->ssl_stek_file)) {
stek_rotate_ts = cur_ts;
}
@ -361,7 +362,7 @@ mod_mbedtls_session_ticket_key_check (plugin_data *p, const time_t cur_ts)
mbedtls_cipher_get_key_bitlen(&key->ctx),
MBEDTLS_ENCRYPT);
if (0 != rc) { /* expire key immediately if error occurs */
key->generation_time = cur_ts > ctx->ticket_lifetime
key->generation_time = cur_ts > (unix_time64_t)ctx->ticket_lifetime
? cur_ts - ctx->ticket_lifetime - 1
: 0;
ctx->active = 1 - ctx->active;
@ -2616,7 +2617,7 @@ REQUEST_FUNC(mod_mbedtls_handle_request_reset)
TRIGGER_FUNC(mod_mbedtls_handle_trigger) {
plugin_data * const p = p_d;
const time_t cur_ts = log_epoch_secs;
const unix_time64_t cur_ts = log_epoch_secs;
if (cur_ts & 0x3f) return HANDLER_GO_ON; /*(continue once each 64 sec)*/
UNUSED(srv);
UNUSED(p);

18
src/mod_nss.c

@ -124,8 +124,8 @@ typedef struct {
SECKEYPrivateKey *ssl_pemfile_pkey;
SSLExtraServerCertData ssl_credex;
const buffer *ssl_stapling_file;
time_t ssl_stapling_loadts;
time_t ssl_stapling_nextts;
unix_time64_t ssl_stapling_loadts;
unix_time64_t ssl_stapling_nextts;
SECItemArray OCSPResponses;
SECItem OCSPResponse;
} plugin_cert;
@ -1035,7 +1035,7 @@ mod_nss_expire_stapling_file (server *srv, plugin_cert *pc)
static int
mod_nss_reload_stapling_file (server *srv, plugin_cert *pc, const time_t cur_ts)
mod_nss_reload_stapling_file (server *srv, plugin_cert *pc, const unix_time64_t cur_ts)
{
SECItem f;
int rc = mod_nss_load_file(pc->ssl_stapling_file->ptr, &f, srv->errh);
@ -1068,11 +1068,11 @@ mod_nss_reload_stapling_file (server *srv, plugin_cert *pc, const time_t cur_ts)
* XXX: *not* implementing our own ASN.1 DER decoder for OCSP response
* ssl.stapling-file will be reloaded hourly
*/
time_t nextupd = (time_t)-1;
unix_time64_t nextupd = -1;
pc->ssl_stapling_loadts = cur_ts;
pc->ssl_stapling_nextts = nextupd;
if (pc->ssl_stapling_nextts == (time_t)-1) {
if (pc->ssl_stapling_nextts == -1) {
/* "Next Update" might not be provided by OCSP responder
* Use 3600 sec (1 hour) in that case. */
/* retry in 1 hour if unable to determine Next Update */
@ -1087,13 +1087,13 @@ mod_nss_reload_stapling_file (server *srv, plugin_cert *pc, const time_t cur_ts)
static int
mod_nss_refresh_stapling_file (server *srv, plugin_cert *pc, const time_t cur_ts)
mod_nss_refresh_stapling_file (server *srv, plugin_cert *pc, const unix_time64_t cur_ts)
{
if (pc->ssl_stapling_nextts > cur_ts + 256)
return 0; /* skip check for refresh unless close to expire */
struct stat st;
if (0 != stat(pc->ssl_stapling_file->ptr, &st)
|| st.st_mtime <= pc->ssl_stapling_loadts) {
|| TIME64_CAST(st.st_mtime) <= pc->ssl_stapling_loadts) {
if (pc->ssl_stapling_nextts < cur_ts)
mod_nss_expire_stapling_file(srv, pc);
return 0;
@ -1103,7 +1103,7 @@ mod_nss_refresh_stapling_file (server *srv, plugin_cert *pc, const time_t cur_ts
static void
mod_nss_refresh_stapling_files (server *srv, const plugin_data *p, const time_t cur_ts)
mod_nss_refresh_stapling_files (server *srv, const plugin_data *p, const unix_time64_t cur_ts)
{
/* future: might construct array of (plugin_cert *) at startup
* to avoid the need to search for them here */
@ -2734,7 +2734,7 @@ REQUEST_FUNC(mod_nss_handle_request_reset)
TRIGGER_FUNC(mod_nss_handle_trigger) {
const plugin_data * const p = p_d;
const time_t cur_ts = log_epoch_secs;
const unix_time64_t cur_ts = log_epoch_secs;
if (cur_ts & 0x3f) return HANDLER_GO_ON; /*(continue once each 64 sec)*/
mod_nss_refresh_stapling_files(srv, p, cur_ts);

69
src/mod_openssl.c

@ -102,8 +102,8 @@ typedef struct {
const buffer *ssl_pemfile;
const buffer *ssl_privkey;
const buffer *ssl_stapling_file;
time_t ssl_stapling_loadts;
time_t ssl_stapling_nextts;
unix_time64_t ssl_stapling_loadts;
unix_time64_t ssl_stapling_nextts;
char must_staple;
char self_issued;
} plugin_cert;
@ -215,19 +215,19 @@ handler_ctx_free (handler_ctx *hctx)
/* openssl has a huge number of interfaces, but not the most useful;
* construct our own session ticket encryption key structure */
typedef struct tlsext_ticket_key_st {
time_t active_ts; /* tickets not issued w/ key until activation timestamp */
time_t expire_ts; /* key not valid after expiration timestamp */
unix_time64_t active_ts; /* tickets not issued w/ key until activation ts*/
unix_time64_t expire_ts; /* key not valid after expiration timestamp */
unsigned char tick_key_name[TLSEXT_KEYNAME_LENGTH];
unsigned char tick_hmac_key[TLSEXT_TICK_KEY_LENGTH];
unsigned char tick_aes_key[TLSEXT_TICK_KEY_LENGTH];
} tlsext_ticket_key_t;
static tlsext_ticket_key_t session_ticket_keys[4];
static time_t stek_rotate_ts;
static unix_time64_t stek_rotate_ts;
static int
mod_openssl_session_ticket_key_generate (time_t active_ts, time_t expire_ts)
mod_openssl_session_ticket_key_generate (unix_time64_t active_ts, unix_time64_t expire_ts)
{
/* openssl RAND_*bytes() functions are called multiple times since the
* funcs might have a 32-byte limit on number of bytes returned each call
@ -275,7 +275,7 @@ mod_openssl_session_ticket_key_rotate (void)
static tlsext_ticket_key_t *
tlsext_ticket_key_get (void)
{
const time_t cur_ts = log_epoch_secs;
const unix_time64_t cur_ts = log_epoch_secs;
const int e = sizeof(session_ticket_keys)/sizeof(*session_ticket_keys) - 1;
for (int i = 0; i < e; ++i) {
if (session_ticket_keys[i].active_ts > cur_ts) continue;
@ -290,7 +290,7 @@ static tlsext_ticket_key_t *
tlsext_ticket_key_find (unsigned char key_name[16], int *refresh)
{
*refresh = 0;
const time_t cur_ts = log_epoch_secs;
const unix_time64_t cur_ts = log_epoch_secs;
const int e = sizeof(session_ticket_keys)/sizeof(*session_ticket_keys) - 1;
for (int i = 0; i < e; ++i) {
if (session_ticket_keys[i].expire_ts < cur_ts) continue;
@ -304,7 +304,7 @@ tlsext_ticket_key_find (unsigned char key_name[16], int *refresh)
static void
tlsext_ticket_wipe_expired (const time_t cur_ts)
tlsext_ticket_wipe_expired (const unix_time64_t cur_ts)
{
const int e = sizeof(session_ticket_keys)/sizeof(*session_ticket_keys) - 1;
for (int i = 0; i < e; ++i) {
@ -422,8 +422,8 @@ mod_openssl_session_ticket_key_file (const char *fn)
if (0 != fdevent_load_file_bytes((char *)buf,(off_t)sizeof(buf),0,fn,NULL))
return rc;
if (buf[0] == 0) { /*(format version 0)*/
session_ticket_keys[3].active_ts = buf[1];
session_ticket_keys[3].expire_ts = buf[2];
session_ticket_keys[3].active_ts = TIME64_CAST(buf[1]);
session_ticket_keys[3].expire_ts = TIME64_CAST(buf[2]);
#ifndef __COVERITY__ /* intentional; hide from Coverity Scan */
/* intentionally copy 80 bytes into consecutive arrays
* tick_key_name[], tick_hmac_key[], tick_aes_key[] */
@ -438,9 +438,9 @@ mod_openssl_session_ticket_key_file (const char *fn)
static void
mod_openssl_session_ticket_key_check (const plugin_data *p, const time_t cur_ts)
mod_openssl_session_ticket_key_check (const plugin_data *p, const unix_time64_t cur_ts)
{
static time_t detect_retrograde_ts;
static unix_time64_t detect_retrograde_ts;
if (detect_retrograde_ts > cur_ts && detect_retrograde_ts - cur_ts > 28800)
stek_rotate_ts = 0;
detect_retrograde_ts = cur_ts;
@ -448,7 +448,8 @@ mod_openssl_session_ticket_key_check (const plugin_data *p, const time_t cur_ts)
int rotate = 0;
if (p->ssl_stek_file) {
struct stat st;
if (0 == stat(p->ssl_stek_file, &st) && st.st_mtime > stek_rotate_ts)
if (0 == stat(p->ssl_stek_file, &st)
&& TIME64_CAST(st.st_mtime) > stek_rotate_ts)
rotate = mod_openssl_session_ticket_key_file(p->ssl_stek_file);
tlsext_ticket_wipe_expired(cur_ts);
}
@ -1405,7 +1406,7 @@ mod_openssl_load_stapling_file (const char *file, log_error_st *errh, buffer *b)
#if !defined(BORINGSSL_API_VERSION)
static time_t
static unix_time64_t
mod_openssl_asn1_time_to_posix (ASN1_TIME *asn1time)
{
#ifdef LIBRESSL_VERSION_NUMBER
@ -1416,7 +1417,7 @@ mod_openssl_asn1_time_to_posix (ASN1_TIME *asn1time)
/*(Note: incorrectly assumes GMT if 'Z' or offset not provided)*/
/*(Note: incorrectly ignores if local timezone might be in DST)*/
if (NULL == asn1time || NULL == asn1time->data) return (time_t)-1;
if (NULL == asn1time || NULL == asn1time->data) return -1;
const char *s = (const char *)asn1time->data;
size_t len = strlen(s);
struct tm x;
@ -1425,20 +1426,20 @@ mod_openssl_asn1_time_to_posix (ASN1_TIME *asn1time)
x.tm_wday = 0;
switch (asn1time->type) {
case V_ASN1_UTCTIME: /* 2-digit year */
if (len < 8) return (time_t)-1;
if (len < 8) return -1;
len -= 8;
x.tm_year = (s[0]-'0')*10 + (s[1]-'0');
x.tm_year += (x.tm_year < 50 ? 2000 : 1900);
s += 2;
break;
case V_ASN1_GENERALIZEDTIME: /* 4-digit year */
if (len < 10) return (time_t)-1;
if (len < 10) return -1;
len -= 10;
x.tm_year = (s[0]-'0')*1000+(s[1]-'0')*100+(s[2]-'0')*10+(s[3]-'0');
s += 4;
break;
default:
return (time_t)-1;
return -1;
}
x.tm_mon = (s[0]-'0')*10 + (s[1]-'0');
x.tm_mday = (s[2]-'0')*10 + (s[3]-'0');
@ -1468,16 +1469,16 @@ mod_openssl_asn1_time_to_posix (ASN1_TIME *asn1time)
if (*s == '-') offset = -offset;
}
else if (s[0] != '\0' && (s[0] != 'Z' || s[1] != '\0'))
return (time_t)-1;
return -1;
if (x.tm_year == 9999 && x.tm_mon == 12 && x.tm_mday == 31
&& x.tm_hour == 23 && x.tm_min == 59 && x.tm_sec == 59 && s[0] == 'Z')
return (time_t)-1; // 99991231235959Z RFC 5280
return -1; // 99991231235959Z RFC 5280
x.tm_year-= 1900;
x.tm_mon -= 1;
time_t t = timegm(&x);
return (t != (time_t)-1) ? t + offset : t;
return (t != -1) ? TIME64_CAST(t) + offset : t;
#else
@ -1485,34 +1486,34 @@ mod_openssl_asn1_time_to_posix (ASN1_TIME *asn1time)
int day, sec;
return ASN1_TIME_diff(&day, &sec, NULL, asn1time)
? log_epoch_secs + day*86400 + sec
: (time_t)-1;
: -1;
#endif
}
#endif
static time_t
static unix_time64_t
mod_openssl_ocsp_next_update (plugin_cert *pc)
{
#if defined(BORINGSSL_API_VERSION)
UNUSED(pc);
return (time_t)-1; /*(not implemented)*/
return -1; /*(not implemented)*/
#else
buffer *der = pc->ssl_stapling;
const unsigned char *p = (unsigned char *)der->ptr; /*(p gets modified)*/
OCSP_RESPONSE *ocsp = d2i_OCSP_RESPONSE(NULL, &p, buffer_clen(der));
if (NULL == ocsp) return (time_t)-1;
if (NULL == ocsp) return -1;
OCSP_BASICRESP *bs = OCSP_response_get1_basic(ocsp);
if (NULL == bs) {
OCSP_RESPONSE_free(ocsp);
return (time_t)-1;
return -1;
}
/* XXX: should save and evaluate cert status returned by these calls */
ASN1_TIME *nextupd = NULL;
OCSP_single_get0_status(OCSP_resp_get0(bs, 0), NULL, NULL, NULL, &nextupd);
time_t t = nextupd ? mod_openssl_asn1_time_to_posix(nextupd) : (time_t)-1;
unix_time64_t t = nextupd ? mod_openssl_asn1_time_to_posix(nextupd) : -1;
/* Note: trust external process which creates ssl.stapling-file to verify
* (as well as to validate certificate status)
@ -1545,7 +1546,7 @@ mod_openssl_expire_stapling_file (server *srv, plugin_cert *pc)
static int
mod_openssl_reload_stapling_file (server *srv, plugin_cert *pc, const time_t cur_ts)
mod_openssl_reload_stapling_file (server *srv, plugin_cert *pc, const unix_time64_t cur_ts)
{
buffer *b = mod_openssl_load_stapling_file(pc->ssl_stapling_file->ptr,
srv->errh, pc->ssl_stapling);
@ -1554,7 +1555,7 @@ mod_openssl_reload_stapling_file (server *srv, plugin_cert *pc, const time_t cur