1
0
mirror of https://github.com/eclipse/mosquitto.git synced 2025-05-09 01:01:11 +08:00

Adding OCSP Stapling support to mosquitto

Adding OCSP Stapling support to mosquitto, so that the TLS client side
requests the certificate status and checks it.
This code uses the OpenSSL-based OCSP implementation and is somewhat
based on the libcurl code for OCSP stapling.

Signed-off-by: Dr. Lars Voelker <lars.voelker@bmw.de>
This commit is contained in:
Dr. Lars Voelker 2017-06-09 15:52:50 +02:00
parent 8de5ed4464
commit 74adb43cc1
17 changed files with 255 additions and 4 deletions

View File

@ -32,7 +32,7 @@ add_library(libmosquitto SHARED
mosquitto.c mosquitto.h
mosquitto_internal.h
mqtt3_protocol.h
net_mosq.c net_mosq.h
net_mosq_ocsp.c net_mosq.c net_mosq.h
read_handle.c read_handle.h
read_handle_client.c
read_handle_shared.c

View File

@ -6,6 +6,7 @@ MOSQ_OBJS=mosquitto.o \
logging_mosq.o \
memory_mosq.o \
messages_mosq.o \
net_mosq_ocsp.o \
net_mosq.o \
read_handle.o \
read_handle_client.o \
@ -59,6 +60,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 $@

View File

@ -305,4 +305,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);
}
}

View File

@ -78,6 +78,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);

View File

@ -77,4 +77,5 @@ MOSQ_1.4 {
mosquitto_pub_topic_check;
mosquitto_sub_topic_check;
mosquitto_socks5_set;
mosquitto_tls_ocsp_set;
} MOSQ_1.3;

View File

@ -203,6 +203,7 @@ int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_se
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);
@ -833,6 +834,24 @@ int mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk, const char *i
}
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_loop(struct mosquitto *mosq, int timeout, int max_packets)
{
#ifdef HAVE_PSELECT

View File

@ -80,7 +80,8 @@ enum mosq_err_t {
MOSQ_ERR_UNKNOWN = 13,
MOSQ_ERR_ERRNO = 14,
MOSQ_ERR_EAI = 15,
MOSQ_ERR_PROXY = 16
MOSQ_ERR_PROXY = 16,
MOSQ_ERR_OCSP = 17
};
/* Error values */
@ -1086,6 +1087,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 <mosquitto_connect>.
*
* 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:
* <mosquitto_tls_set>
*/
libmosq_EXPORT int mosquitto_tls_ocsp_set(struct mosquitto *mosq, int ocsp_reqs);
/*
* Function: mosquitto_connect_callback_set
*

View File

@ -187,6 +187,7 @@ struct mosquitto {
char *tls_psk_identity;
int tls_cert_reqs;
bool tls_insecure;
bool tls_ocsp_required;
#endif
bool want_write;
bool want_connect;

View File

@ -465,6 +465,23 @@ int mosquitto__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) {
_mosquitto_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) {
_mosquitto_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) {
_mosquitto_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);

View File

@ -90,6 +90,7 @@ int _mosquitto_packet_read(struct mosquitto *mosq);
#ifdef WITH_TLS
int _mosquitto_socket_apply_tls(struct mosquitto *mosq);
int mosquitto__socket_connect_tls(struct mosquitto *mosq);
int _mosquitto_verify_ocsp_status_cb(SSL * ssl, void *arg);
#endif
#endif

159
lib/net_mosq_ocsp.c Normal file
View File

@ -0,0 +1,159 @@
/*
Copyright (c) 2009-2014 Roger Light <roger@atchoo.org>
Copyright (c) 2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG), Dr. Lars Voelker <lars.voelker@bmw.de>
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, <daniel@haxx.se>, 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 <openssl/safestack.h>
#include <openssl/tls1.h>
#include <openssl/ssl.h>
#include <openssl/ocsp.h>
#include <logging_mosq.h>
#include <mosquitto_internal.h>
#include <net_mosq.h>
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);
_mosquitto_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) {
_mosquitto_log_printf(mosq, MOSQ_LOG_DEBUG, "OCSP: no response");
goto end;
}
rsp = d2i_OCSP_RESPONSE(NULL, &cp, len);
if (rsp==NULL) {
_mosquitto_log_printf(mosq, MOSQ_LOG_DEBUG, "OCSP: invalid response");
goto end;
}
ocsp_status = OCSP_response_status(rsp);
if(ocsp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
_mosquitto_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) {
_mosquitto_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) {
_mosquitto_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) {
_mosquitto_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);
_mosquitto_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)) {
_mosquitto_log_printf(mosq, MOSQ_LOG_DEBUG, "OCSP: OCSP response has expired");
goto end;
}
break;
case V_OCSP_CERTSTATUS_REVOKED:
_mosquitto_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:
_mosquitto_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

View File

@ -1410,6 +1410,13 @@ topic clients/total in 0 test/mosquitto/org $SYS/broker/
connection to succeed.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>bridge_require_ocsp</option> [ true | false ]</term>
<listitem>
<para>When set to true, the bridge requires OCSP on the TLS
connection it opens as client.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect2>
</refsect1>

View File

@ -13,7 +13,7 @@ set (MOSQ_SRCS
mosquitto.c
mosquitto_broker.h
net.c
../lib/net_mosq.c ../lib/net_mosq.h
../lib/net_mosq_ocsp.c ../lib/net_mosq.c ../lib/net_mosq.h
persist.c persist.h
read_handle.c read_handle_client.c read_handle_server.c
../lib/read_handle_shared.c ../lib/read_handle.h

View File

@ -8,7 +8,7 @@ else
all : mosquitto
endif
mosquitto : mosquitto.o bridge.o conf.o context.o database.o logging.o loop.o memory_mosq.o persist.o net.o net_mosq.o read_handle.o read_handle_client.o read_handle_server.o read_handle_shared.o security.o security_default.o send_client_mosq.o send_mosq.o send_server.o service.o subs.o sys_tree.o time_mosq.o tls_mosq.o util_mosq.o websockets.o will_mosq.o
mosquitto : mosquitto.o bridge.o conf.o context.o database.o logging.o loop.o memory_mosq.o persist.o net.o net_mosq_ocsp.o net_mosq.o read_handle.o read_handle_client.o read_handle_server.o read_handle_shared.o security.o security_default.o send_client_mosq.o send_mosq.o send_server.o service.o subs.o sys_tree.o time_mosq.o tls_mosq.o util_mosq.o websockets.o will_mosq.o
${CROSS_COMPILE}${CC} $^ -o $@ ${LDFLAGS} $(BROKER_LIBS)
mosquitto.o : mosquitto.c mosquitto_broker.h
@ -38,6 +38,9 @@ memory_mosq.o : ../lib/memory_mosq.c ../lib/memory_mosq.h
net.o : net.c mosquitto_broker.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 $@

View File

@ -111,6 +111,7 @@ int mqtt3_bridge_new(struct mosquitto_db *db, struct _mqtt3_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 REAL_WITH_TLS_PSK

View File

@ -781,6 +781,17 @@ int _config_read_file_core(struct mqtt3_config *config, bool reload, const char
}
#else
_mosquitto_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){
_mosquitto_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
_mosquitto_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)

View File

@ -335,6 +335,7 @@ struct _mqtt3_bridge{
char *tls_certfile;
char *tls_keyfile;
bool tls_insecure;
bool tls_ocsp_required;
char *tls_version;
# ifdef REAL_WITH_TLS_PSK
char *tls_psk_identity;