diff --git a/lib/context.c b/lib/context.c index 23b64ab0..04c95a15 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 7fb742d7..1b980dc1 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 0aaa505a..f6f57cb1 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 960c37db..c623eee3 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 e2efee88..62644f55 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 81dc0e23..c900c44f 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 ca2fe3eb..b5826fcb 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(