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:
parent
8de5ed4464
commit
74adb43cc1
@ -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
|
||||
|
@ -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 $@
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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
159
lib/net_mosq_ocsp.c
Normal 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
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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 $@
|
||||
|
||||
|
@ -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
|
||||
|
11
src/conf.c
11
src/conf.c
@ -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)
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user