diff --git a/lib/context.c b/lib/context.c
index 23b64ab00cb58f1a32281c83b8af7c550f14ff57..04c95a15bc4fb8a3dd1479c33d0d56c88624e736 100644
--- a/lib/context.c
+++ b/lib/context.c
@@ -196,7 +196,7 @@ lws_protocol_init(struct lws_context *context)
struct lws_vhost *vh = context->vhost_list;
const struct lws_protocol_vhost_options *pvo, *pvo1;
struct lws wsi;
- int n;
+ int n, any = 0;
if (context->doing_protocol_init)
return 0;
@@ -212,7 +212,8 @@ lws_protocol_init(struct lws_context *context)
wsi.vhost = vh;
/* only do the protocol init once for a given vhost */
- if (vh->created_vhost_protocols)
+ if (vh->created_vhost_protocols ||
+ (vh->options & LWS_SERVER_OPTION_SKIP_PROTOCOL_INIT))
goto next;
/* initialize supported protocols on this vhost */
@@ -260,6 +261,10 @@ lws_protocol_init(struct lws_context *context)
pvo = pvo1->options;
}
+#if defined(LWS_OPENSSL_SUPPORT)
+ any |= !!vh->ssl_ctx;
+#endif
+
/*
* inform all the protocols that they are doing their
* one-time initialization if they want to.
@@ -289,6 +294,9 @@ next:
context->protocol_init_done = 1;
+ if (any)
+ lws_tls_check_all_cert_lifetimes(context);
+
return 0;
}
@@ -1270,6 +1278,8 @@ lws_create_context(struct lws_context_creation_info *info)
LWS_EXT_CB_CLIENT_CONTEXT_CONSTRUCT, NULL, 0) < 0)
goto bail;
+ time(&context->last_cert_check_s);
+
#if defined(LWS_WITH_SELFTESTS)
lws_jws_selftest();
#endif
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 7fb742d7c9957a53aba3961e4e6e273bd91ed661..1b980dc17353b36d3279c5b3d3d42b9431d9e673 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -1432,12 +1432,45 @@ lws_rx_flow_allow_all_protocol(const struct lws_context *context,
}
}
+int
+lws_broadcast(struct lws_context *context, int reason, void *in, size_t len)
+{
+ struct lws_vhost *v = context->vhost_list;
+ struct lws wsi;
+ int n, ret = 0;
+
+ memset(&wsi, 0, sizeof(wsi));
+ wsi.context = context;
+
+ while (v) {
+ const struct lws_protocols *p = v->protocols;
+ wsi.vhost = v;
+
+ for (n = 0; n < v->count_protocols; n++) {
+ wsi.protocol = p;
+ if (p->callback &&
+ p->callback(&wsi, reason, NULL, in, len))
+ ret |= 1;
+ p++;
+ }
+ v = v->vhost_next;
+ }
+
+ return ret;
+}
+
LWS_VISIBLE extern const char *
lws_canonical_hostname(struct lws_context *context)
{
return (const char *)context->canonical_hostname;
}
+LWS_VISIBLE LWS_EXTERN const char *
+lws_get_vhost_name(struct lws_vhost *vhost)
+{
+ return vhost->name;
+}
+
int user_callback_handle_rxflow(lws_callback_function callback_function,
struct lws *wsi,
enum lws_callback_reasons reason, void *user,
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 0aaa505a2edc532e4010abbd7118b8a230780b91..f6f57cb19d8d57b43e7eb9dfef834f82948d0d5f 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -1422,6 +1422,12 @@ enum lws_callback_reasons {
* callback is serialized in the lws event loop normally, even
* if the lws_cancel_service[_pt]() call was from a different
* thread. */
+ LWS_CALLBACK_VHOST_CERT_AGING = 72,
+ /**< When a vhost TLS cert has its expiry checked, this callback
+ * is broadcast to every protocol of every vhost in case the
+ * protocol wants to take some action with this information.
+ * \p in is the lws_vhost and \p len is the number of days left
+ * before it expires, as a (ssize_t) */
/****** add new things just above ---^ ******/
@@ -2504,6 +2510,11 @@ enum lws_context_options {
* LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS callback, which
* provides the vhost SSL_CTX * in the user parameter.
*/
+ LWS_SERVER_OPTION_SKIP_PROTOCOL_INIT = (1 << 25),
+ /**< (VH) You probably don't want this. It forces this vhost to not
+ * call LWS_CALLBACK_PROTOCOL_INIT on its protocols. It's used in the
+ * special case of a temporary vhost bound to a single protocol.
+ */
/****** add new things just above ---^ ******/
};
@@ -3013,6 +3024,14 @@ lws_vhost_get(struct lws *wsi) LWS_WARN_DEPRECATED;
LWS_VISIBLE LWS_EXTERN struct lws_vhost *
lws_get_vhost(struct lws *wsi);
+/**
+ * lws_get_vhost_name() - returns the name of a vhost
+ *
+ * \param vhost: which vhost
+ */
+LWS_VISIBLE LWS_EXTERN const char *
+lws_get_vhost_name(struct lws_vhost *vhost);
+
/**
* lws_json_dump_vhost() - describe vhost state and stats in JSON
*
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 960c37dbc5c4ad6f5991e987b162bfe96e6d83ce..c623eee31692f72406aeeca35be1a860f2b93a65 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -1088,6 +1088,7 @@ struct lws_peer {
struct lws_context {
time_t last_timeout_check_s;
time_t last_ws_ping_pong_check_s;
+ time_t last_cert_check_s;
time_t time_up;
const struct lws_plat_file_ops *fops;
struct lws_plat_file_ops fops_platform;
@@ -2368,6 +2369,7 @@ LWS_EXTERN void lwsl_emit_stderr(int level, const char *line);
#define lws_ssl_remove_wsi_from_buffered_list(_a)
#define lws_context_init_ssl_library(_a)
#define lws_ssl_anybody_has_buffered_read_tsi(_a, _b) (0)
+#define lws_tls_check_all_cert_lifetimes(_a)
#else
#define LWS_SSL_ENABLED(context) (context->use_ssl)
LWS_EXTERN int openssl_websocket_private_data_index;
@@ -2408,6 +2410,8 @@ lws_ssl_info_callback(const lws_tls_conn *ssl, int where, int ret);
LWS_EXTERN int
lws_tls_openssl_cert_info(X509 *x509, enum lws_tls_cert_info type,
union lws_tls_cert_info_results *buf, size_t len);
+LWS_EXTERN int
+lws_tls_check_all_cert_lifetimes(struct lws_context *context);
#ifndef LWS_NO_SERVER
LWS_EXTERN int
lws_context_init_server_ssl(struct lws_context_creation_info *info,
@@ -2526,6 +2530,9 @@ lws_ssl_capable_write_no_ssl(struct lws *wsi, unsigned char *buf, int len);
LWS_EXTERN int LWS_WARN_UNUSED_RESULT
lws_ssl_pending_no_ssl(struct lws *wsi);
+int
+lws_tls_check_cert_lifetime(struct lws_vhost *vhost);
+
int lws_jws_selftest(void);
#ifdef LWS_WITH_HTTP_PROXY
@@ -2704,6 +2711,9 @@ lws_same_vh_protocol_remove(struct lws *wsi);
LWS_EXTERN void
lws_same_vh_protocol_insert(struct lws *wsi, int n);
+LWS_EXTERN int
+lws_broadcast(struct lws_context *context, int reason, void *in, size_t len);
+
#if defined(LWS_WITH_STATS)
void
lws_stats_atomic_bump(struct lws_context * context,
diff --git a/lib/service.c b/lib/service.c
index e2efee887c7b0010712f6700dd3501074ba4ebda..62644f55e913eab93b9ea1202254f9f975548fbe 100644
--- a/lib/service.c
+++ b/lib/service.c
@@ -1166,6 +1166,14 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
}
}
+ /*
+ * check the remaining cert lifetime daily
+ */
+ if (context->last_cert_check_s < now - (24 * 60 * 60)) {
+ context->last_cert_check_s = now;
+
+ lws_tls_check_all_cert_lifetimes(context);
+ }
/* the socket we came to service timed out, nothing to do */
if (timed_out)
@@ -1234,7 +1242,6 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
switch (wsi->mode) {
case LWSCM_EVENT_PIPE:
{
- struct lws_vhost *v = context->vhost_list;
#if !defined(WIN32) && !defined(_WIN32)
char s[10];
@@ -1248,31 +1255,18 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
if (n < 0)
goto close_and_handled;
#endif
-
/*
* the poll() wait, or the event loop for libuv etc is a
* process-wide resource that we interrupted. So let every
* protocol that may be interested in the pipe event know that
* it happened.
*/
- while (v) {
- const struct lws_protocols *p = v->protocols;
- wsi->vhost = v;
-
- for (n = 0; n < v->count_protocols; n++) {
- wsi->protocol = p;
- if (p->callback && p->callback(wsi,
- LWS_CALLBACK_EVENT_WAIT_CANCELLED,
- NULL, NULL, 0)) {
- lwsl_info("closed in event cancel\n");
- goto close_and_handled;
- }
- p++;
- }
- v = v->vhost_next;
+ if (lws_broadcast(context, LWS_CALLBACK_EVENT_WAIT_CANCELLED,
+ NULL, 0)) {
+ lwsl_info("closed in event cancel\n");
+ goto close_and_handled;
}
- wsi->vhost = NULL;
- wsi->protocol = NULL;
+
goto handled;
}
case LWSCM_HTTP_SERVING:
diff --git a/lib/tls/tls.c b/lib/tls/tls.c
index 81dc0e231ad550999e1ad498ae85e3d40ebf5642..c900c44fc2df695939ecbda68de2dc48f8e8c586 100644
--- a/lib/tls/tls.c
+++ b/lib/tls/tls.c
@@ -21,8 +21,9 @@
#include "private-libwebsockets.h"
-int lws_alloc_vfs_file(struct lws_context *context, const char *filename, uint8_t **buf,
- lws_filepos_t *amount)
+int
+lws_alloc_vfs_file(struct lws_context *context, const char *filename,
+ uint8_t **buf, lws_filepos_t *amount)
{
lws_filepos_t len;
lws_fop_flags_t flags = LWS_O_RDONLY;
@@ -97,6 +98,45 @@ lws_ssl_remove_wsi_from_buffered_list(struct lws *wsi)
wsi->pending_read_list_next = NULL;
}
+int
+lws_tls_check_cert_lifetime(struct lws_vhost *v)
+{
+ union lws_tls_cert_info_results ir;
+ time_t now = (time_t)lws_now_secs(), life;
+ int n;
+
+ if (!v->ssl_ctx)
+ return -1;
+
+ if (now < 1464083026) /* May 2016 */
+ /* our clock is wrong and we can't judge the certs */
+ return -1;
+
+ n = lws_tls_vhost_cert_info(v, LWS_TLS_CERT_INFO_VALIDITY_TO, &ir, 0);
+ if (n)
+ return -1;
+
+ life = (ir.time - now) / (24 * 3600);
+ lwsl_notice(" vhost %s: cert expiry: %dd\n", v->name, (int)life);
+
+ lws_broadcast(v->context, LWS_CALLBACK_VHOST_CERT_AGING, v,
+ (size_t)(ssize_t)life);
+
+ return 0;
+}
+
+int
+lws_tls_check_all_cert_lifetimes(struct lws_context *context)
+{
+ struct lws_vhost *v = context->vhost_list;
+
+ while (v) {
+ lws_tls_check_cert_lifetime(v);
+ v = v->vhost_next;
+ }
+
+ return 0;
+}
int
lws_gate_accepts(struct lws_context *context, int on)
diff --git a/plugins/protocol_lws_sshd_demo.c b/plugins/protocol_lws_sshd_demo.c
index ca2fe3eb8397395b70d4b5acefe71967385639a4..b5826fcb6cd0abc6240819b81a0095515fc341ea 100644
--- a/plugins/protocol_lws_sshd_demo.c
+++ b/plugins/protocol_lws_sshd_demo.c
@@ -411,6 +411,9 @@ callback_lws_sshd_demo(struct lws *wsi, enum lws_callback_reasons reason,
close(vhd->privileged_fd);
break;
+ case LWS_CALLBACK_VHOST_CERT_AGING:
+ break;
+
default:
if (!vhd->ssh_base_protocol) {
vhd->ssh_base_protocol = lws_vhost_name_to_protocol(