diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 895910ddf..1a053d37f 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -29,7 +29,7 @@ set(C_SRC mosquitto.c mosquitto.h mosquitto_internal.h mqtt_protocol.h - net_mosq.c net_mosq.h + net_mosq_ocsp.c net_mosq.c net_mosq.h options.c packet_datatypes.c packet_mosq.c packet_mosq.h diff --git a/lib/Makefile b/lib/Makefile index efce2c984..eaf1b4b06 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -21,6 +21,7 @@ MOSQ_OBJS=mosquitto.o \ loop.o \ memory_mosq.o \ messages_mosq.o \ + net_mosq_ocsp.o \ net_mosq.o \ options.o \ packet_datatypes.o \ @@ -151,6 +152,9 @@ messages_mosq.o : messages_mosq.c messages_mosq.h memory_mosq.o : memory_mosq.c memory_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ +net_mosq_ocsp.o : net_mosq_ocsp.c net_mosq.h + ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ + net_mosq.o : net_mosq.c net_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CFLAGS) -c $< -o $@ diff --git a/lib/cpp/mosquittopp.cpp b/lib/cpp/mosquittopp.cpp index e1c6cc5db..c93ee04cc 100644 --- a/lib/cpp/mosquittopp.cpp +++ b/lib/cpp/mosquittopp.cpp @@ -378,4 +378,8 @@ int mosquittopp::tls_psk_set(const char *psk, const char *identity, const char * return mosquitto_tls_psk_set(m_mosq, psk, identity, ciphers); } +int mosquittopp::tls_ocsp_set(int ocsp_reqs) +{ + return mosquitto_tls_ocsp_set(m_mosq, ocsp_reqs); +} } diff --git a/lib/cpp/mosquittopp.h b/lib/cpp/mosquittopp.h index 163d616fe..863fd80c8 100644 --- a/lib/cpp/mosquittopp.h +++ b/lib/cpp/mosquittopp.h @@ -110,6 +110,7 @@ class mosqpp_EXPORT mosquittopp { int tls_opts_set(int cert_reqs, const char *tls_version=NULL, const char *ciphers=NULL); int tls_insecure_set(bool value); int tls_psk_set(const char *psk, const char *identity, const char *ciphers=NULL); + int tls_ocsp_set(int ocsp_reqs); int opts_set(enum mosq_opt_t option, void *value); int loop(int timeout=-1, int max_packets=1); diff --git a/lib/linker.version b/lib/linker.version index 017d5a5e5..1b1311825 100644 --- a/lib/linker.version +++ b/lib/linker.version @@ -126,6 +126,7 @@ MOSQ_1.6 { mosquitto_subscribe_multiple; mosquitto_subscribe_v5; mosquitto_subscribe_v5_callback_set; + mosquitto_tls_ocsp_set; mosquitto_unsubscribe_multiple; mosquitto_unsubscribe_v5; mosquitto_unsubscribe_v5_callback_set; diff --git a/lib/mosquitto.c b/lib/mosquitto.c index 3fed86523..28b3fe3d5 100644 --- a/lib/mosquitto.c +++ b/lib/mosquitto.c @@ -180,6 +180,7 @@ int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_st mosq->tls_cert_reqs = SSL_VERIFY_PEER; mosq->tls_insecure = false; mosq->want_write = false; + mosq->tls_ocsp_required = false; #endif #ifdef WITH_THREADING pthread_mutex_init(&mosq->callback_mutex, NULL); diff --git a/lib/mosquitto.h b/lib/mosquitto.h index 1a222a805..3d4eddcfb 100644 --- a/lib/mosquitto.h +++ b/lib/mosquitto.h @@ -96,6 +96,7 @@ enum mosq_err_t { MOSQ_ERR_TLS_HANDSHAKE = 23, MOSQ_ERR_QOS_NOT_SUPPORTED = 24, MOSQ_ERR_OVERSIZE_PACKET = 25, + MOSQ_ERR_OCSP = 26, }; /* Error values */ @@ -1724,6 +1725,26 @@ libmosq_EXPORT int mosquitto_tls_opts_set(struct mosquitto *mosq, int cert_reqs, */ libmosq_EXPORT int mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk, const char *identity, const char *ciphers); +/* + * Function: mosquitto_tls_ocsp_set + * + * Set advanced SSL/TLS options. Must be called before . + * + * Parameters: + * mosq - a valid mosquitto instance. + * ocsp_reqs - whether OCSP checking is required: + * 0 - no checking required + * 1 - checking required + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * + * See Also: + * + */ +libmosq_EXPORT int mosquitto_tls_ocsp_set(struct mosquitto *mosq, int ocsp_reqs); + /* ====================================================================== * diff --git a/lib/mosquitto_internal.h b/lib/mosquitto_internal.h index 409ff1e62..93222b8e7 100644 --- a/lib/mosquitto_internal.h +++ b/lib/mosquitto_internal.h @@ -220,6 +220,7 @@ struct mosquitto { int tls_cert_reqs; bool tls_insecure; bool ssl_ctx_defaults; + bool tls_ocsp_required; char *tls_engine; char *tls_engine_kpass_sha1; enum mosquitto__keyform tls_keyform; diff --git a/lib/net_mosq.c b/lib/net_mosq.c index 9708b0d28..be37223dc 100644 --- a/lib/net_mosq.c +++ b/lib/net_mosq.c @@ -476,6 +476,23 @@ int net__socket_connect_tls(struct mosquitto *mosq) int ret, err; ERR_clear_error(); + long res; + if (mosq->tls_ocsp_required) { + // Note: OCSP is available in all currently supported OpenSSL versions. + if ((res=SSL_set_tlsext_status_type(mosq->ssl, TLSEXT_STATUSTYPE_ocsp)) != 1) { + log__printf(mosq, MOSQ_LOG_ERR, "Could not activate OCSP (error: %ld)", res); + return MOSQ_ERR_OCSP; + } + if ((res=SSL_CTX_set_tlsext_status_cb(mosq->ssl_ctx, mosquitto__verify_ocsp_status_cb)) != 1) { + log__printf(mosq, MOSQ_LOG_ERR, "Could not activate OCSP (error: %ld)", res); + return MOSQ_ERR_OCSP; + } + if ((res=SSL_CTX_set_tlsext_status_arg(mosq->ssl_ctx, mosq)) != 1) { + log__printf(mosq, MOSQ_LOG_ERR, "Could not activate OCSP (error: %ld)", res); + return MOSQ_ERR_OCSP; + } + } + ret = SSL_connect(mosq->ssl); if(ret != 1) { err = SSL_get_error(mosq->ssl, ret); diff --git a/lib/net_mosq.h b/lib/net_mosq.h index b860198b5..b843ebe38 100644 --- a/lib/net_mosq.h +++ b/lib/net_mosq.h @@ -71,6 +71,7 @@ ssize_t net__write(struct mosquitto *mosq, void *buf, size_t count); #ifdef WITH_TLS int net__socket_apply_tls(struct mosquitto *mosq); int net__socket_connect_tls(struct mosquitto *mosq); +int mosquitto__verify_ocsp_status_cb(SSL * ssl, void *arg); UI_METHOD *net__get_ui_method(void); #define ENGINE_FINISH(e) if(e) ENGINE_finish(e) #define ENGINE_SECRET_MODE "SECRET_MODE" diff --git a/lib/net_mosq_ocsp.c b/lib/net_mosq_ocsp.c new file mode 100644 index 000000000..76fec3df8 --- /dev/null +++ b/lib/net_mosq_ocsp.c @@ -0,0 +1,159 @@ +/* +Copyright (c) 2009-2014 Roger Light +Copyright (c) 2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG), Dr. Lars Voelker + +All rights reserved. This program and the accompanying materials +are made available under the terms of the Eclipse Public License v1.0 +and Eclipse Distribution License v1.0 which accompany this distribution. + +The Eclipse Public License is available at + http://www.eclipse.org/legal/epl-v10.html +and the Eclipse Distribution License is available at + http://www.eclipse.org/org/documents/edl-v10.php. + +Contributors: + Dr. Lars Voelker, BMW AG +*/ + +/* +COPYRIGHT AND PERMISSION NOTICE of curl on which the ocsp code is based: + +Copyright (c) 1996 - 2016, Daniel Stenberg, , and many +contributors, see the THANKS file. + +All rights reserved. + +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", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN +NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of a copyright holder shall not +be used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization of the copyright holder. +*/ + +#ifdef WITH_TLS +#include +#include +#include +#include + +#include +#include +#include + +int mosquitto__verify_ocsp_status_cb(SSL * ssl, void *arg) +{ + struct mosquitto *mosq = (struct mosquitto *)arg; + int ocsp_status, result2, i; + unsigned char *p; + const unsigned char *cp; + OCSP_RESPONSE *rsp = NULL; + OCSP_BASICRESP *br = NULL; + X509_STORE *st = NULL; + STACK_OF(X509) *ch = NULL; + + long len = SSL_get_tlsext_status_ocsp_resp(mosq->ssl, &p); + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL_get_tlsext_status_ocsp_resp returned %ld bytes", len); + + // the following functions expect a const pointer + cp = (const unsigned char *)p; + + if (!cp || len <= 0) { + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: no response"); + goto end; + } + + + rsp = d2i_OCSP_RESPONSE(NULL, &cp, len); + if (rsp==NULL) { + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: invalid response"); + goto end; + } + + ocsp_status = OCSP_response_status(rsp); + if(ocsp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: invalid status: %s (%d)", + OCSP_response_status_str(ocsp_status), ocsp_status); + goto end; + } + + br = OCSP_response_get1_basic(rsp); + if (!br) { + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: invalid response"); + goto end; + } + + ch = SSL_get_peer_cert_chain(mosq->ssl); + if (sk_X509_num(ch) <= 0) { + log__printf(mosq, MOSQ_LOG_ERR, "OCSP: we did not receive certificates of the server (num: %d)", sk_X509_num(ch)); + goto end; + } + + st = SSL_CTX_get_cert_store(mosq->ssl_ctx); + + // Note: + // Other checkers often fix problems in OpenSSL before 1.0.2a (e.g. libcurl). + // For all currently supported versions of the OpenSSL project, this is not needed anymore. + + if ((result2=OCSP_basic_verify(br, ch, st, 0)) <= 0) { + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: response verification failed (error: %d)", result2); + goto end; + } + + for(i = 0; i < OCSP_resp_count(br); i++) { + int cert_status, crl_reason; + OCSP_SINGLERESP *single = NULL; + + ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; + + single = OCSP_resp_get0(br, i); + if(!single) + continue; + + cert_status = OCSP_single_get0_status(single, &crl_reason, &rev, &thisupd, &nextupd); + + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL certificate status: %s (%d)", + OCSP_cert_status_str(cert_status), cert_status); + + switch(cert_status) { + case V_OCSP_CERTSTATUS_GOOD: + // Note: A OCSP stapling result will be accepted up to 5 minutes after it expired! + if(!OCSP_check_validity(thisupd, nextupd, 300L, -1L)) { + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: OCSP response has expired"); + goto end; + } + break; + + case V_OCSP_CERTSTATUS_REVOKED: + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL certificate revocation reason: %s (%d)", + OCSP_crl_reason_str(crl_reason), crl_reason); + goto end; + + case V_OCSP_CERTSTATUS_UNKNOWN: + goto end; + + default: + log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL certificate revocation status unknown"); + goto end; + } + } + + if (br!=NULL) OCSP_BASICRESP_free(br); + if (rsp!=NULL) OCSP_RESPONSE_free(rsp); + return 1; // OK + +end: + if (br!=NULL) OCSP_BASICRESP_free(br); + if (rsp!=NULL) OCSP_RESPONSE_free(rsp); + return 0; // Not OK +} +#endif diff --git a/lib/options.c b/lib/options.c index 23e89e711..2720a43a9 100644 --- a/lib/options.c +++ b/lib/options.c @@ -195,6 +195,24 @@ int mosquitto_tls_set(struct mosquitto *mosq, const char *cafile, const char *ca } +int mosquitto_tls_ocsp_set(struct mosquitto *mosq, int ocsp_reqs) +{ +#ifdef WITH_TLS + if (ocsp_reqs==0) { + mosq->tls_ocsp_required = false; + return MOSQ_ERR_SUCCESS; + } + + if (ocsp_reqs==1) { + mosq->tls_ocsp_required = true; + return MOSQ_ERR_SUCCESS; + } +#endif + + return MOSQ_ERR_INVAL; +} + + int mosquitto_tls_opts_set(struct mosquitto *mosq, int cert_reqs, const char *tls_version, const char *ciphers) { #ifdef WITH_TLS diff --git a/man/mosquitto.conf.5.xml b/man/mosquitto.conf.5.xml index a6b52fbd2..d69b639d5 100644 --- a/man/mosquitto.conf.5.xml +++ b/man/mosquitto.conf.5.xml @@ -1792,6 +1792,13 @@ topic clients/total in 0 test/mosquitto/org $SYS/broker/ connection to succeed. + + [ true | false ] + + When set to true, the bridge requires OCSP on the TLS + connection it opens as client. + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 28d1430c2..23952fa10 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,7 +27,7 @@ set (MOSQ_SRCS mosquitto.c mosquitto_broker.h mosquitto_broker_internal.h net.c - ../lib/net_mosq.c ../lib/net_mosq.h + ../lib/net_mosq_ocsp.c ../lib/net_mosq.c ../lib/net_mosq.h ../lib/packet_datatypes.c ../lib/packet_mosq.c ../lib/packet_mosq.h persist_read_v234.c persist_read_v5.c persist_read.c diff --git a/src/Makefile b/src/Makefile index 2dce3cb05..60f4b131e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -32,6 +32,7 @@ OBJS= mosquitto.o \ memory_mosq.o \ net.o \ net_mosq.o \ + net_mosq_ocsp.o \ packet_datatypes.o \ packet_mosq.o \ property_broker.o \ @@ -140,6 +141,9 @@ memory_mosq.o : ../lib/memory_mosq.c ../lib/memory_mosq.h net.o : net.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ +net_mosq_ocsp.o : ../lib/net_mosq_ocsp.c ../lib/net_mosq.h + ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ + net_mosq.o : ../lib/net_mosq.c ../lib/net_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CFLAGS) -c $< -o $@ diff --git a/src/bridge.c b/src/bridge.c index 5e9a436b3..f55f12f2a 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -84,6 +84,7 @@ int bridge__new(struct mosquitto_db *db, struct mosquitto__bridge *bridge) new_context->tls_certfile = new_context->bridge->tls_certfile; new_context->tls_keyfile = new_context->bridge->tls_keyfile; new_context->tls_cert_reqs = SSL_VERIFY_PEER; + new_context->tls_ocsp_required = new_context->bridge->tls_ocsp_required; new_context->tls_version = new_context->bridge->tls_version; new_context->tls_insecure = new_context->bridge->tls_insecure; #ifdef FINAL_WITH_TLS_PSK diff --git a/src/conf.c b/src/conf.c index 0204c7aad..d53fab0c1 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1023,6 +1023,17 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct } #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TLS-PSK support not available."); +#endif + }else if(!strcmp(token, "bridge_require_ocsp")){ +#if defined(WITH_BRIDGE) && defined(WITH_TLS) + if(reload) continue; // Listeners not valid for reloading. + if(!cur_bridge){ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); + return MOSQ_ERR_INVAL; + } + if(conf__parse_bool(&token, "bridge_require_ocsp", &cur_bridge->tls_ocsp_required, saveptr)) return MOSQ_ERR_INVAL; +#else + log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "bridge_keyfile")){ #if defined(WITH_BRIDGE) && defined(WITH_TLS) diff --git a/src/mosquitto_broker_internal.h b/src/mosquitto_broker_internal.h index 99449e4ad..dedcf61b4 100644 --- a/src/mosquitto_broker_internal.h +++ b/src/mosquitto_broker_internal.h @@ -486,6 +486,7 @@ struct mosquitto__bridge{ bool initial_notification_done; #ifdef WITH_TLS bool tls_insecure; + bool tls_ocsp_required; char *tls_cafile; char *tls_capath; char *tls_certfile;