From 0de3f3a3f7cf8dae92950e271954841be336a01e Mon Sep 17 00:00:00 2001 From: Konstantin Podsvirov Date: Sat, 6 Nov 2021 16:31:20 +0300 Subject: [PATCH 01/79] Allow to build man pages on Windows Why not if xsltproc available? Moreover why try to install not existent files? Signed-off-by: Konstantin Podsvirov --- man/CMakeLists.txt | 75 +++++++++++++++++++++++----------------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/man/CMakeLists.txt b/man/CMakeLists.txt index 2c8b0955..a5b93e35 100644 --- a/man/CMakeLists.txt +++ b/man/CMakeLists.txt @@ -4,44 +4,45 @@ # could not be found, then the man pages will not be built or installed - # because the install is optional. -if(NOT WIN32) - find_program(XSLTPROC xsltproc OPTIONAL) - if(XSLTPROC) - function(compile_manpage page) - add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/man/${page} - COMMAND xsltproc ${CMAKE_SOURCE_DIR}/man/${page}.xml -o ${CMAKE_SOURCE_DIR}/man/ - MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/man/${page}.xml) - add_custom_target(${page} ALL DEPENDS ${CMAKE_SOURCE_DIR}/man/${page}) - endfunction() +find_program(XSLTPROC xsltproc OPTIONAL) +if(XSLTPROC) + function(compile_manpage page) + add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/man/${page} + COMMAND xsltproc ${CMAKE_SOURCE_DIR}/man/${page}.xml -o ${CMAKE_SOURCE_DIR}/man/ + MAIN_DEPENDENCY ${CMAKE_SOURCE_DIR}/man/${page}.xml) + add_custom_target(${page} ALL DEPENDS ${CMAKE_SOURCE_DIR}/man/${page}) + endfunction() - compile_manpage("mosquitto_ctrl.1") - compile_manpage("mosquitto_ctrl_dynsec.1") - compile_manpage("mosquitto_passwd.1") - compile_manpage("mosquitto_pub.1") - compile_manpage("mosquitto_sub.1") - compile_manpage("mosquitto_rr.1") - compile_manpage("libmosquitto.3") - compile_manpage("mosquitto.conf.5") - compile_manpage("mosquitto-tls.7") - compile_manpage("mqtt.7") - compile_manpage("mosquitto.8") - else() - message(FATAL_ERROR "xsltproc not found: manpages cannot be built") - endif() + compile_manpage("mosquitto_ctrl.1") + compile_manpage("mosquitto_ctrl_dynsec.1") + compile_manpage("mosquitto_passwd.1") + compile_manpage("mosquitto_pub.1") + compile_manpage("mosquitto_sub.1") + compile_manpage("mosquitto_rr.1") + compile_manpage("libmosquitto.3") + compile_manpage("mosquitto.conf.5") + compile_manpage("mosquitto-tls.7") + compile_manpage("mqtt.7") + compile_manpage("mosquitto.8") + install(FILES + mosquitto_ctrl.1 + mosquitto_ctrl_dynsec.1 + mosquitto_passwd.1 + mosquitto_pub.1 + mosquitto_sub.1 + mosquitto_rr.1 + DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 + OPTIONAL) + + install(FILES libmosquitto.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3 OPTIONAL) + install(FILES mosquitto.conf.5 DESTINATION ${CMAKE_INSTALL_MANDIR}/man5 OPTIONAL) + install(FILES mosquitto-tls.7 mqtt.7 DESTINATION ${CMAKE_INSTALL_MANDIR}/man7 OPTIONAL) + install(FILES mosquitto.8 DESTINATION ${CMAKE_INSTALL_MANDIR}/man8 OPTIONAL) + +elseif(WIN32) + message(WARNING "xsltproc not found: manpages cannot be built") +else() + message(FATAL_ERROR "xsltproc not found: manpages cannot be built") endif() -install(FILES - mosquitto_ctrl.1 - mosquitto_ctrl_dynsec.1 - mosquitto_passwd.1 - mosquitto_pub.1 - mosquitto_sub.1 - mosquitto_rr.1 - DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 - OPTIONAL) - -install(FILES libmosquitto.3 DESTINATION ${CMAKE_INSTALL_MANDIR}/man3 OPTIONAL) -install(FILES mosquitto.conf.5 DESTINATION ${CMAKE_INSTALL_MANDIR}/man5 OPTIONAL) -install(FILES mosquitto-tls.7 mqtt.7 DESTINATION ${CMAKE_INSTALL_MANDIR}/man7 OPTIONAL) -install(FILES mosquitto.8 DESTINATION ${CMAKE_INSTALL_MANDIR}/man8 OPTIONAL) From b3e26c21d72b6ced0424a10a0c736ab7140b888a Mon Sep 17 00:00:00 2001 From: Muhammed Hashim Date: Sat, 13 Aug 2022 11:24:59 +0530 Subject: [PATCH 02/79] Correct a wrong example mosquitto_ctrl example for setClientId was provided with wrong example (duplicate of setClientPassword). --- plugins/dynamic-security/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dynamic-security/README.md b/plugins/dynamic-security/README.md index 6949d59c..8e48621b 100644 --- a/plugins/dynamic-security/README.md +++ b/plugins/dynamic-security/README.md @@ -244,7 +244,7 @@ Command: mosquitto_ctrl example: ``` -mosquitto_ctrl dynsec setClientPassword username password +mosquitto_ctrl dynsec setClientId username clientId ``` ## Set Client Password From 1af3152ddceb71cd604f779ba90fa0ff02194d88 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Thu, 18 Aug 2022 14:04:59 +0100 Subject: [PATCH 03/79] Fix persisted clients being restored without a session expiry time. --- src/session_expiry.c | 44 ++++++++++++-------- test/broker/06-bridge-reconnect-local-out.py | 1 + 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/session_expiry.c b/src/session_expiry.c index 98913d8a..d5df0535 100644 --- a/src/session_expiry.c +++ b/src/session_expiry.c @@ -43,6 +43,26 @@ static int session_expiry__cmp(struct session_expiry_list *i1, struct session_ex } +static void set_session_expiry_time(struct mosquitto *context) +{ + context->session_expiry_time = db.now_real_s; + + if(db.config->persistent_client_expiration == 0){ + /* No global expiry, so use the client expiration interval */ + context->session_expiry_time += context->session_expiry_interval; + }else{ + /* We have a global expiry interval */ + if(db.config->persistent_client_expiration < context->session_expiry_interval){ + /* The client expiry is longer than the global expiry, so use the global */ + context->session_expiry_time += db.config->persistent_client_expiration; + }else{ + /* The global expiry is longer than the client expiry, so use the client */ + context->session_expiry_time += context->session_expiry_interval; + } + } +} + + int session_expiry__add(struct mosquitto *context) { struct session_expiry_list *item; @@ -59,21 +79,7 @@ int session_expiry__add(struct mosquitto *context) if(!item) return MOSQ_ERR_NOMEM; item->context = context; - item->context->session_expiry_time = db.now_real_s; - - if(db.config->persistent_client_expiration == 0){ - /* No global expiry, so use the client expiration interval */ - item->context->session_expiry_time += item->context->session_expiry_interval; - }else{ - /* We have a global expiry interval */ - if(db.config->persistent_client_expiration < item->context->session_expiry_interval){ - /* The client expiry is longer than the global expiry, so use the global */ - item->context->session_expiry_time += db.config->persistent_client_expiration; - }else{ - /* The global expiry is longer than the client expiry, so use the client */ - item->context->session_expiry_time += item->context->session_expiry_interval; - } - } + set_session_expiry_time(item->context); context->expiry_list_item = item; DL_INSERT_INORDER(expiry_list, item, session_expiry__cmp); @@ -98,7 +104,12 @@ int session_expiry__add_from_persistence(struct mosquitto *context, time_t expir if(!item) return MOSQ_ERR_NOMEM; item->context = context; - item->context->session_expiry_time = expiry_time; + if(expiry_time){ + item->context->session_expiry_time = expiry_time; + }else{ + set_session_expiry_time(item->context); + + } context->expiry_list_item = item; DL_INSERT_INORDER(expiry_list, item, session_expiry__cmp); @@ -165,4 +176,3 @@ void session_expiry__check(void) } } } - diff --git a/test/broker/06-bridge-reconnect-local-out.py b/test/broker/06-bridge-reconnect-local-out.py index 887470b6..b5adba8f 100755 --- a/test/broker/06-bridge-reconnect-local-out.py +++ b/test/broker/06-bridge-reconnect-local-out.py @@ -17,6 +17,7 @@ def write_config(filename, port1, port2, protocol_version): f.write("address 127.0.0.1:%d\n" % (port1)) f.write("topic bridge/# out\n") f.write("bridge_protocol_version %s\n" % (protocol_version)) + f.write("cleansession false\n") def do_test(proto_ver): From ef44b22cefbc846db58419243436a3b1248cb7af Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Thu, 18 Aug 2022 15:05:14 +0100 Subject: [PATCH 04/79] Fix local bridge session expiry interval. --- src/bridge.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/bridge.c b/src/bridge.c index f6e77d32..70c7ffe5 100644 --- a/src/bridge.c +++ b/src/bridge.c @@ -126,6 +126,9 @@ int bridge__new(struct mosquitto__bridge *bridge) } new_context->retain_available = bridge->outgoing_retain; new_context->protocol = bridge->protocol_version; + if(!bridge->clean_start_local){ + new_context->session_expiry_interval = UINT32_MAX; + } bridges = mosquitto__realloc(db.bridges, (size_t)(db.bridge_count+1)*sizeof(struct mosquitto *)); if(bridges){ From 86fffa34a9db7c513a96d220273c5a2ef8f620f0 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Mon, 22 Aug 2022 15:57:14 +0100 Subject: [PATCH 05/79] Fix $SYS messages being expired after 60 seconds and hence unchanged values disappearing. Thanks to Wim Nelis and Christoph Krey. --- ChangeLog.txt | 4 ++++ src/sys_tree.c | 54 +++++++++++++++++++++++++------------------------- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index ffe19b80..c7bb3516 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,7 @@ +Broker: +- Fix $SYS messages being expired after 60 seconds and hence unchanged values + disappearing. + 2.0.15 - 2022-08-16 =================== diff --git a/src/sys_tree.c b/src/sys_tree.c index fd339214..419d425f 100644 --- a/src/sys_tree.c +++ b/src/sys_tree.c @@ -76,31 +76,31 @@ static void sys_tree__update_clients(char *buf) if(client_count != count_total){ client_count = count_total; len = (uint32_t)snprintf(buf, BUFLEN, "%d", client_count); - db__messages_easy_queue(NULL, "$SYS/broker/clients/total", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/clients/total", SYS_TREE_QOS, len, buf, 1, 0, NULL); if(client_count > client_max){ client_max = client_count; len = (uint32_t)snprintf(buf, BUFLEN, "%d", client_max); - db__messages_easy_queue(NULL, "$SYS/broker/clients/maximum", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/clients/maximum", SYS_TREE_QOS, len, buf, 1, 0, NULL); } } if(disconnected_count != count_total-count_by_sock){ disconnected_count = count_total-count_by_sock; len = (uint32_t)snprintf(buf, BUFLEN, "%d", disconnected_count); - db__messages_easy_queue(NULL, "$SYS/broker/clients/inactive", SYS_TREE_QOS, len, buf, 1, 60, NULL); - db__messages_easy_queue(NULL, "$SYS/broker/clients/disconnected", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/clients/inactive", SYS_TREE_QOS, len, buf, 1, 0, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/clients/disconnected", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(connected_count != count_by_sock){ connected_count = count_by_sock; len = (uint32_t)snprintf(buf, BUFLEN, "%d", connected_count); - db__messages_easy_queue(NULL, "$SYS/broker/clients/active", SYS_TREE_QOS, len, buf, 1, 60, NULL); - db__messages_easy_queue(NULL, "$SYS/broker/clients/connected", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/clients/active", SYS_TREE_QOS, len, buf, 1, 0, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/clients/connected", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(g_clients_expired != clients_expired){ clients_expired = g_clients_expired; len = (uint32_t)snprintf(buf, BUFLEN, "%d", clients_expired); - db__messages_easy_queue(NULL, "$SYS/broker/clients/expired", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/clients/expired", SYS_TREE_QOS, len, buf, 1, 0, NULL); } } @@ -116,13 +116,13 @@ static void sys_tree__update_memory(char *buf) if(current_heap != value_ul){ current_heap = value_ul; len = (uint32_t)snprintf(buf, BUFLEN, "%lu", current_heap); - db__messages_easy_queue(NULL, "$SYS/broker/heap/current", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/heap/current", SYS_TREE_QOS, len, buf, 1, 0, NULL); } value_ul =mosquitto__max_memory_used(); if(max_heap != value_ul){ max_heap = value_ul; len = (uint32_t)snprintf(buf, BUFLEN, "%lu", max_heap); - db__messages_easy_queue(NULL, "$SYS/broker/heap/maximum", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/heap/maximum", SYS_TREE_QOS, len, buf, 1, 0, NULL); } } #endif @@ -135,12 +135,12 @@ static void calc_load(char *buf, const char *topic, bool initial, double exponen if (initial) { new_value = *current; len = (uint32_t)snprintf(buf, BUFLEN, "%.2f", new_value); - db__messages_easy_queue(NULL, topic, SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, topic, SYS_TREE_QOS, len, buf, 1, 0, NULL); } else { new_value = interval + exponent*((*current) - interval); if(fabs(new_value - (*current)) >= 0.01){ len = (uint32_t)snprintf(buf, BUFLEN, "%.2f", new_value); - db__messages_easy_queue(NULL, topic, SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, topic, SYS_TREE_QOS, len, buf, 1, 0, NULL); } } (*current) = new_value; @@ -218,7 +218,7 @@ void sys_tree__update(int interval, time_t start_time) if(interval && db.now_s - interval > last_update){ uptime = db.now_s - start_time; len = (uint32_t)snprintf(buf, BUFLEN, "%d seconds", (int)uptime); - db__messages_easy_queue(NULL, "$SYS/broker/uptime", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/uptime", SYS_TREE_QOS, len, buf, 1, 0, NULL); sys_tree__update_clients(buf); initial_publish = false; @@ -287,32 +287,32 @@ void sys_tree__update(int interval, time_t start_time) if(db.msg_store_count != msg_store_count){ msg_store_count = db.msg_store_count; len = (uint32_t)snprintf(buf, BUFLEN, "%d", msg_store_count); - db__messages_easy_queue(NULL, "$SYS/broker/messages/stored", SYS_TREE_QOS, len, buf, 1, 60, NULL); - db__messages_easy_queue(NULL, "$SYS/broker/store/messages/count", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/messages/stored", SYS_TREE_QOS, len, buf, 1, 0, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/store/messages/count", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if (db.msg_store_bytes != msg_store_bytes){ msg_store_bytes = db.msg_store_bytes; len = (uint32_t)snprintf(buf, BUFLEN, "%lu", msg_store_bytes); - db__messages_easy_queue(NULL, "$SYS/broker/store/messages/bytes", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/store/messages/bytes", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(db.subscription_count != subscription_count){ subscription_count = db.subscription_count; len = (uint32_t)snprintf(buf, BUFLEN, "%d", subscription_count); - db__messages_easy_queue(NULL, "$SYS/broker/subscriptions/count", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/subscriptions/count", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(db.shared_subscription_count != shared_subscription_count){ shared_subscription_count = db.shared_subscription_count; len = (uint32_t)snprintf(buf, BUFLEN, "%d", shared_subscription_count); - db__messages_easy_queue(NULL, "$SYS/broker/shared_subscriptions/count", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/shared_subscriptions/count", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(db.retained_count != retained_count){ retained_count = db.retained_count; len = (uint32_t)snprintf(buf, BUFLEN, "%d", retained_count); - db__messages_easy_queue(NULL, "$SYS/broker/retained messages/count", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/retained messages/count", SYS_TREE_QOS, len, buf, 1, 0, NULL); } #ifdef REAL_WITH_MEMORY_TRACKING @@ -322,55 +322,55 @@ void sys_tree__update(int interval, time_t start_time) if(msgs_received != g_msgs_received){ msgs_received = g_msgs_received; len = (uint32_t)snprintf(buf, BUFLEN, "%lu", msgs_received); - db__messages_easy_queue(NULL, "$SYS/broker/messages/received", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/messages/received", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(msgs_sent != g_msgs_sent){ msgs_sent = g_msgs_sent; len = (uint32_t)snprintf(buf, BUFLEN, "%lu", msgs_sent); - db__messages_easy_queue(NULL, "$SYS/broker/messages/sent", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/messages/sent", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(publish_dropped != g_msgs_dropped){ publish_dropped = g_msgs_dropped; len = (uint32_t)snprintf(buf, BUFLEN, "%lu", publish_dropped); - db__messages_easy_queue(NULL, "$SYS/broker/publish/messages/dropped", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/publish/messages/dropped", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(pub_msgs_received != g_pub_msgs_received){ pub_msgs_received = g_pub_msgs_received; len = (uint32_t)snprintf(buf, BUFLEN, "%lu", pub_msgs_received); - db__messages_easy_queue(NULL, "$SYS/broker/publish/messages/received", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/publish/messages/received", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(pub_msgs_sent != g_pub_msgs_sent){ pub_msgs_sent = g_pub_msgs_sent; len = (uint32_t)snprintf(buf, BUFLEN, "%lu", pub_msgs_sent); - db__messages_easy_queue(NULL, "$SYS/broker/publish/messages/sent", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/publish/messages/sent", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(bytes_received != g_bytes_received){ bytes_received = g_bytes_received; len = (uint32_t)snprintf(buf, BUFLEN, "%llu", bytes_received); - db__messages_easy_queue(NULL, "$SYS/broker/bytes/received", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/bytes/received", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(bytes_sent != g_bytes_sent){ bytes_sent = g_bytes_sent; len = (uint32_t)snprintf(buf, BUFLEN, "%llu", bytes_sent); - db__messages_easy_queue(NULL, "$SYS/broker/bytes/sent", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/bytes/sent", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(pub_bytes_received != g_pub_bytes_received){ pub_bytes_received = g_pub_bytes_received; len = (uint32_t)snprintf(buf, BUFLEN, "%llu", pub_bytes_received); - db__messages_easy_queue(NULL, "$SYS/broker/publish/bytes/received", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/publish/bytes/received", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(pub_bytes_sent != g_pub_bytes_sent){ pub_bytes_sent = g_pub_bytes_sent; len = (uint32_t)snprintf(buf, BUFLEN, "%llu", pub_bytes_sent); - db__messages_easy_queue(NULL, "$SYS/broker/publish/bytes/sent", SYS_TREE_QOS, len, buf, 1, 60, NULL); + db__messages_easy_queue(NULL, "$SYS/broker/publish/bytes/sent", SYS_TREE_QOS, len, buf, 1, 0, NULL); } last_update = db.now_s; From dcad2e410a92b610c2519a6a149c690b611ea7fd Mon Sep 17 00:00:00 2001 From: Arvin Date: Wed, 24 Aug 2022 22:47:57 +0800 Subject: [PATCH 06/79] Fix comments errors, it is the client that sends PING to broker actively --- include/mosquitto.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/include/mosquitto.h b/include/mosquitto.h index 2d34976c..2cc64cef 100644 --- a/include/mosquitto.h +++ b/include/mosquitto.h @@ -497,8 +497,8 @@ libmosq_EXPORT int mosquitto_username_pw_set(struct mosquitto *mosq, const char * mosq - a valid mosquitto instance. * host - the hostname or ip address of the broker to connect to. * port - the network port to connect to. Usually 1883. - * keepalive - the number of seconds after which the broker should send a PING - * message to the client if no other messages have been exchanged + * keepalive - the number of seconds after which the client should send a PING + * message to the broker if no other messages have been exchanged * in that time. * * Returns: @@ -529,8 +529,8 @@ libmosq_EXPORT int mosquitto_connect(struct mosquitto *mosq, const char *host, i * mosq - a valid mosquitto instance. * host - the hostname or ip address of the broker to connect to. * port - the network port to connect to. Usually 1883. - * keepalive - the number of seconds after which the broker should send a PING - * message to the client if no other messages have been exchanged + * keepalive - the number of seconds after which the client should send a PING + * message to the broker if no other messages have been exchanged * in that time. * bind_address - the hostname or ip address of the local network interface to * bind to. If you do not want to bind to a specific interface, @@ -573,8 +573,8 @@ libmosq_EXPORT int mosquitto_connect_bind(struct mosquitto *mosq, const char *ho * mosq - a valid mosquitto instance. * host - the hostname or ip address of the broker to connect to. * port - the network port to connect to. Usually 1883. - * keepalive - the number of seconds after which the broker should send a PING - * message to the client if no other messages have been exchanged + * keepalive - the number of seconds after which the client should send a PING + * message to the broker if no other messages have been exchanged * in that time. * bind_address - the hostname or ip address of the local network interface to * bind to. If you do not want to bind to a specific interface, @@ -614,8 +614,8 @@ libmosq_EXPORT int mosquitto_connect_bind_v5(struct mosquitto *mosq, const char * mosq - a valid mosquitto instance. * host - the hostname or ip address of the broker to connect to. * port - the network port to connect to. Usually 1883. - * keepalive - the number of seconds after which the broker should send a PING - * message to the client if no other messages have been exchanged + * keepalive - the number of seconds after which the client should send a PING + * message to the broker if no other messages have been exchanged * in that time. * * Returns: @@ -649,8 +649,8 @@ libmosq_EXPORT int mosquitto_connect_async(struct mosquitto *mosq, const char *h * mosq - a valid mosquitto instance. * host - the hostname or ip address of the broker to connect to. * port - the network port to connect to. Usually 1883. - * keepalive - the number of seconds after which the broker should send a PING - * message to the client if no other messages have been exchanged + * keepalive - the number of seconds after which the client should send a PING + * message to the broker if no other messages have been exchanged * in that time. * bind_address - the hostname or ip address of the local network interface to * bind to. If you do not want to bind to a specific interface, @@ -688,8 +688,8 @@ libmosq_EXPORT int mosquitto_connect_bind_async(struct mosquitto *mosq, const ch * Parameters: * mosq - a valid mosquitto instance. * host - the hostname to search for an SRV record. - * keepalive - the number of seconds after which the broker should send a PING - * message to the client if no other messages have been exchanged + * keepalive - the number of seconds after which the client should send a PING + * message to the broker if no other messages have been exchanged * in that time. * bind_address - the hostname or ip address of the local network interface to * bind to. If you do not want to bind to a specific interface, From e22030bb2cfc2e2af564e7ab75d532f7aaa2f9da Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Wed, 31 Aug 2022 09:49:37 +0100 Subject: [PATCH 07/79] Try to convince Coverity this string is terminated. --- src/persist_read_v234.c | 2 +- src/persist_read_v5.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/persist_read_v234.c b/src/persist_read_v234.c index 7460c309..49147bba 100644 --- a/src/persist_read_v234.c +++ b/src/persist_read_v234.c @@ -180,9 +180,9 @@ int persist__chunk_msg_store_read_v234(FILE *db_fptr, struct P_msg_store *chunk, log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } + read_e(db_fptr, chunk->payload, chunk->F.payloadlen); /* Ensure zero terminated regardless of contents */ ((uint8_t *)chunk->payload)[chunk->F.payloadlen] = 0; - read_e(db_fptr, chunk->payload, chunk->F.payloadlen); } return MOSQ_ERR_SUCCESS; diff --git a/src/persist_read_v5.c b/src/persist_read_v5.c index abc9a580..173046ed 100644 --- a/src/persist_read_v5.c +++ b/src/persist_read_v5.c @@ -203,9 +203,9 @@ int persist__chunk_msg_store_read_v56(FILE *db_fptr, struct P_msg_store *chunk, log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } + read_e(db_fptr, chunk->payload, chunk->F.payloadlen); /* Ensure zero terminated regardless of contents */ ((uint8_t *)chunk->payload)[chunk->F.payloadlen] = 0; - read_e(db_fptr, chunk->payload, chunk->F.payloadlen); } if(length > 0){ From f44d8fad50c966b5743d6d4d8fe907c842f8e823 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Wed, 31 Aug 2022 10:00:05 +0100 Subject: [PATCH 08/79] Fix Coverity 1491742 and 1491741 --- src/logging.c | 2 +- src/sys_tree.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/logging.c b/src/logging.c index 6795d7e9..5f22df71 100644 --- a/src/logging.c +++ b/src/logging.c @@ -291,7 +291,7 @@ static int log__vprintf(unsigned int priority, const char *fmt, va_list va) log_line_pos = (size_t)snprintf(log_line, sizeof(log_line), "Time error"); } }else{ - log_line_pos = (size_t)snprintf(log_line, sizeof(log_line), "%d", (int)db.now_real_s); + log_line_pos = (size_t)snprintf(log_line, sizeof(log_line), "%" PRIu64, (uint64_t)db.now_real_s); } if(log_line_pos < sizeof(log_line)-3){ log_line[log_line_pos] = ':'; diff --git a/src/sys_tree.c b/src/sys_tree.c index 419d425f..5350fa56 100644 --- a/src/sys_tree.c +++ b/src/sys_tree.c @@ -217,7 +217,7 @@ void sys_tree__update(int interval, time_t start_time) if(interval && db.now_s - interval > last_update){ uptime = db.now_s - start_time; - len = (uint32_t)snprintf(buf, BUFLEN, "%d seconds", (int)uptime); + len = (uint32_t)snprintf(buf, BUFLEN, "%" PRIu64 " seconds", (uint64_t)uptime); db__messages_easy_queue(NULL, "$SYS/broker/uptime", SYS_TREE_QOS, len, buf, 1, 0, NULL); sys_tree__update_clients(buf); From 5c3c5d779fd07d10b7e778dbaeecf30fd30e7a87 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Wed, 31 Aug 2022 11:36:53 +0100 Subject: [PATCH 09/79] Fix Coverity 1486949 --- lib/handle_pubackcomp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/handle_pubackcomp.c b/lib/handle_pubackcomp.c index b2dcf7ee..4568bb40 100644 --- a/lib/handle_pubackcomp.c +++ b/lib/handle_pubackcomp.c @@ -100,6 +100,7 @@ int handle__pubackcomp(struct mosquitto *mosq, const char *type) && reason_code != MQTT_RC_PAYLOAD_FORMAT_INVALID ){ + mosquitto_property_free_all(&properties); return MOSQ_ERR_PROTOCOL; } }else{ @@ -107,14 +108,13 @@ int handle__pubackcomp(struct mosquitto *mosq, const char *type) && reason_code != MQTT_RC_PACKET_ID_NOT_FOUND ){ + mosquitto_property_free_all(&properties); return MOSQ_ERR_PROTOCOL; } } } if(mosq->in_packet.pos < mosq->in_packet.remaining_length){ -#ifdef WITH_BROKER mosquitto_property_free_all(&properties); -#endif return MOSQ_ERR_MALFORMED_PACKET; } From 28416da47ae3ef4dc00f0c924887d6b22ce90a4c Mon Sep 17 00:00:00 2001 From: Alexander Fiebig Date: Sat, 8 Oct 2022 19:08:21 +0200 Subject: [PATCH 10/79] Fixed empty string crash in mosquitto_property_copy_all Signed-off-by: Alexander Fiebig --- lib/property_mosq.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/property_mosq.c b/lib/property_mosq.c index 71c49737..7afe08cc 100644 --- a/lib/property_mosq.c +++ b/lib/property_mosq.c @@ -1248,7 +1248,7 @@ int mosquitto_property_copy_all(mosquitto_property **dest, const mosquitto_prope case MQTT_PROP_SERVER_REFERENCE: case MQTT_PROP_REASON_STRING: pnew->value.s.len = src->value.s.len; - pnew->value.s.v = strdup(src->value.s.v); + pnew->value.s.v = src->value.s.v ? strdup(src->value.s.v) : (char*)calloc(1,1); if(!pnew->value.s.v){ mosquitto_property_free_all(dest); return MOSQ_ERR_NOMEM; @@ -1268,14 +1268,14 @@ int mosquitto_property_copy_all(mosquitto_property **dest, const mosquitto_prope case MQTT_PROP_USER_PROPERTY: pnew->value.s.len = src->value.s.len; - pnew->value.s.v = strdup(src->value.s.v); + pnew->value.s.v = src->value.s.v ? strdup(src->value.s.v) : (char*)calloc(1,1); if(!pnew->value.s.v){ mosquitto_property_free_all(dest); return MOSQ_ERR_NOMEM; } pnew->name.len = src->name.len; - pnew->name.v = strdup(src->name.v); + pnew->name.v = src->name.v ? strdup(src->name.v) : (char*)calloc(1,1); if(!pnew->name.v){ mosquitto_property_free_all(dest); return MOSQ_ERR_NOMEM; From 0cee0d1d11b8f26897a1b017db8ce24005005fd9 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Wed, 26 Oct 2022 10:06:38 +0100 Subject: [PATCH 11/79] Fix Coverity Scan 1486944 (backport from develop) --- lib/misc_mosq.c | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/lib/misc_mosq.c b/lib/misc_mosq.c index 5004125a..2529d794 100644 --- a/lib/misc_mosq.c +++ b/lib/misc_mosq.c @@ -22,6 +22,8 @@ Contributors: #include "config.h" #include +#include +#include #include #include #include @@ -33,8 +35,10 @@ Contributors: # include # include # include +# define PATH_MAX MAX_PATH #else # include +# include #endif #include "misc_mosq.h" @@ -126,30 +130,33 @@ FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read) } } #else - if(mode[0] == 'r'){ - struct stat statbuf; - if(stat(path, &statbuf) < 0){ - return NULL; - } - - if(!S_ISREG(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)){ - log__printf(NULL, MOSQ_LOG_ERR, "Error: %s is not a file.", path); - return NULL; - } - } + FILE *fptr; + struct stat statbuf; if (restrict_read) { - FILE *fptr; mode_t old_mask; old_mask = umask(0077); fptr = fopen(path, mode); umask(old_mask); - - return fptr; }else{ - return fopen(path, mode); + fptr = fopen(path, mode); } + if(!fptr) return NULL; + + if(fstat(fileno(fptr), &statbuf) < 0){ + fclose(fptr); + return NULL; + } + + if(!S_ISREG(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)){ +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_ERR, "Error: %s is not a file.", path); +#endif + fclose(fptr); + return NULL; + } + return fptr; #endif } From 9018f12dfcc03c0baad6a3abd7ba7dbee780f692 Mon Sep 17 00:00:00 2001 From: Guillaume PELLEGRINO Date: Wed, 9 Nov 2022 09:51:26 +0100 Subject: [PATCH 12/79] Fix memory leak in mosquitto_tls_opts_set() When calling mosquitto_tls_opts_set() multiple time in a row on the same mosquitto context (Example: trying to reconnect to mqtt broker), some of the tls options are not free, resulting in a memory leak. The TLS options (tls_version, tls_ciphers) must be free before being set. Signed-off-by: Guillaume PELLEGRINO --- lib/options.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/options.c b/lib/options.c index fa7386c2..23c88352 100644 --- a/lib/options.c +++ b/lib/options.c @@ -228,19 +228,23 @@ int mosquitto_tls_opts_set(struct mosquitto *mosq, int cert_reqs, const char *tl || !strcasecmp(tls_version, "tlsv1.2") || !strcasecmp(tls_version, "tlsv1.1")){ + mosquitto__free(mosq->tls_version); mosq->tls_version = mosquitto__strdup(tls_version); if(!mosq->tls_version) return MOSQ_ERR_NOMEM; }else{ return MOSQ_ERR_INVAL; } }else{ + mosquitto__free(mosq->tls_version); mosq->tls_version = mosquitto__strdup("tlsv1.2"); if(!mosq->tls_version) return MOSQ_ERR_NOMEM; } if(ciphers){ + mosquitto__free(mosq->tls_ciphers); mosq->tls_ciphers = mosquitto__strdup(ciphers); if(!mosq->tls_ciphers) return MOSQ_ERR_NOMEM; }else{ + mosquitto__free(mosq->tls_ciphers); mosq->tls_ciphers = NULL; } From d1cdaec963190e5cd2a685bd3992fbd9e31e0182 Mon Sep 17 00:00:00 2001 From: Rony B Chandran Date: Tue, 22 Nov 2022 11:53:31 +0530 Subject: [PATCH 13/79] Removed repeated line ISO-8601 format date and time, e.g. 2016-08-10T09:47:38+0100 --- man/mosquitto_sub.1.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/man/mosquitto_sub.1.xml b/man/mosquitto_sub.1.xml index 6a7c5b38..ee9274e4 100644 --- a/man/mosquitto_sub.1.xml +++ b/man/mosquitto_sub.1.xml @@ -938,7 +938,6 @@ mosquitto_sub -t 'bbc/#' -T bbc/bbc1 --remove-retained If the payload is not valid JSON, then the error message "Error: Message payload is not valid JSON on topic <topic>" will be printed to stderr. - ISO-8601 format date and time, e.g. 2016-08-10T09:47:38+0100 Unix timestamp with nanoseconds, e.g. 1470818943.786368637 From a562c93079de51fea6b408f2313925d2a80bd600 Mon Sep 17 00:00:00 2001 From: zivillian Date: Mon, 21 Nov 2022 21:45:23 +0100 Subject: [PATCH 14/79] fix example JSON Signed-off-by: zivillian --- plugins/dynamic-security/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/dynamic-security/README.md b/plugins/dynamic-security/README.md index 6949d59c..558b2162 100644 --- a/plugins/dynamic-security/README.md +++ b/plugins/dynamic-security/README.md @@ -61,7 +61,7 @@ Command: { "commands":[ { - "command": "getDefaultACLAccess", + "command": "getDefaultACLAccess" } ] } @@ -523,7 +523,7 @@ Command: { "commands":[ { - "command": "getAnonymousGroup", + "command": "getAnonymousGroup" } ] } From c06599fab2544b8c2f81b4e2fa06165d97e54481 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 29 Nov 2022 11:55:55 +0000 Subject: [PATCH 15/79] Fix some retained topic memory not being cleared immediately after used. --- ChangeLog.txt | 1 + src/retain.c | 20 +++++++ test/broker/04-retain-clear-multiple.py | 80 +++++++++++++++++++++++++ test/broker/Makefile | 1 + test/broker/test.py | 1 + 5 files changed, 103 insertions(+) create mode 100755 test/broker/04-retain-clear-multiple.py diff --git a/ChangeLog.txt b/ChangeLog.txt index c7bb3516..a9b46ee4 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,6 +1,7 @@ Broker: - Fix $SYS messages being expired after 60 seconds and hence unchanged values disappearing. +- Fix some retained topic memory not being cleared immediately after used. 2.0.15 - 2022-08-16 =================== diff --git a/src/retain.c b/src/retain.c index e5065daf..6c303c96 100644 --- a/src/retain.c +++ b/src/retain.c @@ -72,6 +72,25 @@ int retain__init(void) } +void retain__clean_empty_hierarchy(struct mosquitto__retainhier *retainhier) +{ + struct mosquitto__retainhier *parent; + + while(retainhier){ + if(retainhier->children || retainhier->retained || retainhier->parent == NULL){ + /* Entry is being used */ + return; + }else{ + HASH_DELETE(hh, retainhier->parent->children, retainhier); + mosquitto__free(retainhier->topic); + parent = retainhier->parent; + mosquitto__free(retainhier); + retainhier = parent; + } + } +} + + int retain__store(const char *topic, struct mosquitto_msg_store *stored, char **split_topics) { struct mosquitto__retainhier *retainhier; @@ -124,6 +143,7 @@ int retain__store(const char *topic, struct mosquitto_msg_store *stored, char ** #endif }else{ retainhier->retained = NULL; + retain__clean_empty_hierarchy(retainhier); } return MOSQ_ERR_SUCCESS; diff --git a/test/broker/04-retain-clear-multiple.py b/test/broker/04-retain-clear-multiple.py new file mode 100755 index 00000000..be185a21 --- /dev/null +++ b/test/broker/04-retain-clear-multiple.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python3 + +# Exercise multi-level retain clearing + +from mosq_test_helper import * + +def send_retain(port, topic, payload): + connect_packet = mosq_test.gen_connect("retain-clear-test") + connack_packet = mosq_test.gen_connack(rc=0) + + publish_packet = mosq_test.gen_publish(topic, qos=1, mid=1, payload=payload, retain=True) + puback_packet = mosq_test.gen_puback(mid=1) + + sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=4, port=port) + mosq_test.do_send_receive(sock, publish_packet, puback_packet, f"set retain {topic}") + sock.close() + +def do_test(): + rc = 1 + connect_packet = mosq_test.gen_connect("retain-clear-test") + connack_packet = mosq_test.gen_connack(rc=0) + + subscribe_packet = mosq_test.gen_subscribe(1, "#", 0) + suback_packet = mosq_test.gen_suback(1, 0) + + retain1_packet = mosq_test.gen_publish("1/2/3/4/5/6/7", qos=0, payload="retained message", retain=True) + retain2_packet = mosq_test.gen_publish("1/2/3/4", qos=0, payload="retained message", retain=True) + retain3_packet = mosq_test.gen_publish("1", qos=0, payload="retained message", retain=True) + + port = mosq_test.get_port() + broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) + + try: + send_retain(port, "1/2/3/4/5/6/7", "retained message") + send_retain(port, "1/2/3/4", "retained message") + send_retain(port, "1", "retained message") + + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + mosq_test.expect_packet(sock, "retain3", retain3_packet) + mosq_test.expect_packet(sock, "retain2", retain2_packet) + mosq_test.expect_packet(sock, "retain1", retain1_packet) + sock.close() + + send_retain(port, "1/2/3/4", None) + + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + mosq_test.expect_packet(sock, "retain3", retain3_packet) + mosq_test.expect_packet(sock, "retain1", retain1_packet) + sock.close() + + send_retain(port, "1/2/3/4/5/6/7", None) + + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + mosq_test.expect_packet(sock, "retain3", retain3_packet) + sock.close() + + send_retain(port, "1", None) + + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + mosq_test.do_ping(sock) + sock.close() + + rc = 0 + except mosq_test.TestError: + pass + finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde.decode('utf-8')) + exit(rc) + +do_test() +exit(0) + diff --git a/test/broker/Makefile b/test/broker/Makefile index 63b9ae8f..b2c86788 100644 --- a/test/broker/Makefile +++ b/test/broker/Makefile @@ -90,6 +90,7 @@ msg_sequence_test: ./04-retain-check-source-persist-diff-port.py ./04-retain-check-source-persist.py ./04-retain-check-source.py + ./04-retain-clear-multiple.py ./04-retain-qos0-clear.py ./04-retain-qos0-fresh.py ./04-retain-qos0-repeated.py diff --git a/test/broker/test.py b/test/broker/test.py index e034a83d..602b97bc 100755 --- a/test/broker/test.py +++ b/test/broker/test.py @@ -68,6 +68,7 @@ tests = [ (1, './04-retain-check-source-persist.py'), (1, './04-retain-check-source.py'), + (1, './04-retain-clear-multiple.py'), (1, './04-retain-qos0-clear.py'), (1, './04-retain-qos0-fresh.py'), (1, './04-retain-qos0-repeated.py'), From 8285a57bb91b9aa06bf7293fc209ef73aca0430d Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Fri, 2 Dec 2022 14:31:40 +0000 Subject: [PATCH 16/79] Fix some error handling related to the `bind_interface` option. --- ChangeLog.txt | 1 + src/net.c | 86 +++++++++++++++++++++++++++-------------------- test/mosq_test.py | 2 +- 3 files changed, 52 insertions(+), 37 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index a9b46ee4..041ed9ac 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -2,6 +2,7 @@ Broker: - Fix $SYS messages being expired after 60 seconds and hence unchanged values disappearing. - Fix some retained topic memory not being cleared immediately after used. +- Fix error handling related to the `bind_interface` option. 2.0.15 - 2022-08-16 =================== diff --git a/src/net.c b/src/net.c index 80d2c8d6..b9a27685 100644 --- a/src/net.c +++ b/src/net.c @@ -622,6 +622,8 @@ static int net__bind_interface(struct mosquitto__listener *listener, struct addr * matching interface in the later bind(). */ struct ifaddrs *ifaddr, *ifa; + bool have_interface = false; + if(getifaddrs(&ifaddr) < 0){ net__print_error(MOSQ_LOG_ERR, "Error: %s"); return MOSQ_ERR_ERRNO; @@ -632,49 +634,56 @@ static int net__bind_interface(struct mosquitto__listener *listener, struct addr continue; } - if(!strcasecmp(listener->bind_interface, ifa->ifa_name) - && ifa->ifa_addr->sa_family == rp->ai_addr->sa_family){ + if(!strcasecmp(listener->bind_interface, ifa->ifa_name)){ + have_interface = true; - if(rp->ai_addr->sa_family == AF_INET){ - if(listener->host && - memcmp(&((struct sockaddr_in *)rp->ai_addr)->sin_addr, - &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, - sizeof(struct in_addr))){ + if(ifa->ifa_addr->sa_family == rp->ai_addr->sa_family){ + if(rp->ai_addr->sa_family == AF_INET){ + if(listener->host && + memcmp(&((struct sockaddr_in *)rp->ai_addr)->sin_addr, + &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, + sizeof(struct in_addr))){ - log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Interface address for %s does not match specified listener address (%s).", - listener->bind_interface, listener->host); - return MOSQ_ERR_INVAL; - }else{ - memcpy(&((struct sockaddr_in *)rp->ai_addr)->sin_addr, - &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, - sizeof(struct in_addr)); + log__printf(NULL, MOSQ_LOG_ERR, "Error: Interface address for %s does not match specified listener address (%s).", + listener->bind_interface, listener->host); + return MOSQ_ERR_INVAL; + }else{ + memcpy(&((struct sockaddr_in *)rp->ai_addr)->sin_addr, + &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, + sizeof(struct in_addr)); - freeifaddrs(ifaddr); - return MOSQ_ERR_SUCCESS; - } - }else if(rp->ai_addr->sa_family == AF_INET6){ - if(listener->host && - memcmp(&((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, - &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, - sizeof(struct in6_addr))){ + freeifaddrs(ifaddr); + return MOSQ_ERR_SUCCESS; + } + }else if(rp->ai_addr->sa_family == AF_INET6){ + if(listener->host && + memcmp(&((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, + &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, + sizeof(struct in6_addr))){ - log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Interface address for %s does not match specified listener address (%s).", - listener->bind_interface, listener->host); - return MOSQ_ERR_INVAL; - }else{ - memcpy(&((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, - &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, - sizeof(struct in6_addr)); - freeifaddrs(ifaddr); - return MOSQ_ERR_SUCCESS; + log__printf(NULL, MOSQ_LOG_ERR, "Error: Interface address for %s does not match specified listener address (%s).", + listener->bind_interface, listener->host); + return MOSQ_ERR_INVAL; + }else{ + memcpy(&((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, + &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, + sizeof(struct in6_addr)); + freeifaddrs(ifaddr); + return MOSQ_ERR_SUCCESS; + } } } } } freeifaddrs(ifaddr); - log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Interface %s does not support %s configuration.", - listener->bind_interface, rp->ai_addr->sa_family == AF_INET ? "IPv4" : "IPv6"); - return MOSQ_ERR_NOT_FOUND; + if(have_interface){ + log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Interface %s does not support %s configuration.", + listener->bind_interface, rp->ai_addr->sa_family == AF_INET ? "IPv4" : "IPv6"); + return MOSQ_ERR_NOT_SUPPORTED; + }else{ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Interface %s does not exist.", listener->bind_interface); + return MOSQ_ERR_NOT_FOUND; + } } #endif @@ -756,10 +765,15 @@ static int net__socket_listen_tcp(struct mosquitto__listener *listener) if(listener->bind_interface){ /* It might be possible that an interface does not support all relevant sa_families. * We should successfully find at least one. */ - if(net__bind_interface(listener, rp)){ + rc = net__bind_interface(listener, rp); + if(rc){ COMPAT_CLOSE(sock); listener->sock_count--; - continue; + if(rc == MOSQ_ERR_NOT_FOUND || rc == MOSQ_ERR_INVAL){ + return rc; + }else{ + continue; + } } interface_bound = true; } diff --git a/test/mosq_test.py b/test/mosq_test.py index d9fe6970..98fd3ac9 100644 --- a/test/mosq_test.py +++ b/test/mosq_test.py @@ -70,7 +70,7 @@ def start_broker(filename, cmd=None, port=0, use_conf=False, expect_fail=False, print("FAIL: unable to start broker: %s" % errs) raise IOError else: - return None + return broker def start_client(filename, cmd, env, port=1888): if cmd is None: From 7ff744d60dccc591bae15a37fa27a13cd4d33a8b Mon Sep 17 00:00:00 2001 From: Rob Swindell Date: Fri, 2 Dec 2022 11:52:34 -0800 Subject: [PATCH 17/79] =?UTF-8?q?Fix=20build=20error:=20expected=20?= =?UTF-8?q?=E2=80=98)=E2=80=99=20before=20=E2=80=98PRIu64=E2=80=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit system with 'make WITH_TLS=no WITH_CJSON=no WITH_DOCS=no' Signed-off-by: Rob Swindell --- src/logging.c | 1 + src/sys_tree.c | 1 + 2 files changed, 2 insertions(+) diff --git a/src/logging.c b/src/logging.c index 5f22df71..f6361553 100644 --- a/src/logging.c +++ b/src/logging.c @@ -20,6 +20,7 @@ Contributors: #include #include #include +#include #ifndef WIN32 #include #endif diff --git a/src/sys_tree.c b/src/sys_tree.c index 5350fa56..c44565a4 100644 --- a/src/sys_tree.c +++ b/src/sys_tree.c @@ -23,6 +23,7 @@ Contributors: #include #include #include +#include #include "mosquitto_broker_internal.h" #include "memory_mosq.h" From 06d583527057f79a423eaffbed29b15fac791b50 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Sat, 3 Dec 2022 22:59:14 +0000 Subject: [PATCH 18/79] Add more WITH_BROKER guards. --- lib/property_mosq.c | 4 ++++ lib/send_publish.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/property_mosq.c b/lib/property_mosq.c index 7afe08cc..0b27c297 100644 --- a/lib/property_mosq.c +++ b/lib/property_mosq.c @@ -141,7 +141,9 @@ static int property__read(struct mosquitto__packet *packet, uint32_t *len, mosqu break; default: +#ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_DEBUG, "Unsupported property type: %d", property_identifier); +#endif return MOSQ_ERR_MALFORMED_PACKET; } @@ -415,7 +417,9 @@ static int property__write(struct mosquitto__packet *packet, const mosquitto_pro break; default: +#ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_DEBUG, "Unsupported property type: %d", property->identifier); +#endif return MOSQ_ERR_INVAL; } diff --git a/lib/send_publish.c b/lib/send_publish.c index 1b5ffdaa..be52474b 100644 --- a/lib/send_publish.c +++ b/lib/send_publish.c @@ -177,7 +177,7 @@ int send__real_publish(struct mosquitto *mosq, uint16_t mid, const char *topic, #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_NOTICE, "Dropping too large outgoing PUBLISH for %s (%d bytes)", SAFE_PRINT(mosq->id), packetlen); #else - log__printf(NULL, MOSQ_LOG_NOTICE, "Dropping too large outgoing PUBLISH (%d bytes)", packetlen); + log__printf(mosq, MOSQ_LOG_NOTICE, "Dropping too large outgoing PUBLISH (%d bytes)", packetlen); #endif return MOSQ_ERR_OVERSIZE_PACKET; } From 62b68364c769ea5544807df7ca8e0c98c2cffec7 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Fri, 16 Dec 2022 16:19:51 +0000 Subject: [PATCH 19/79] Fix std* files not being redirected when daemonising This could occur when built with assertions removed. Closes #2708. Thanks to ckoehne. --- ChangeLog.txt | 2 ++ src/mosquitto.c | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 041ed9ac..1140dd3b 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -3,6 +3,8 @@ Broker: disappearing. - Fix some retained topic memory not being cleared immediately after used. - Fix error handling related to the `bind_interface` option. +- Fix std* files not being redirected when daemonising, when built with + assertions removed. Closes #2708. 2.0.15 - 2022-08-16 =================== diff --git a/src/mosquitto.c b/src/mosquitto.c index 09632f28..4dfd2bde 100644 --- a/src/mosquitto.c +++ b/src/mosquitto.c @@ -160,9 +160,18 @@ static void mosquitto__daemonise(void) exit(1); } - assert(freopen("/dev/null", "r", stdin)); - assert(freopen("/dev/null", "w", stdout)); - assert(freopen("/dev/null", "w", stderr)); + if(!freopen("/dev/null", "r", stdin)){ + log__printf(NULL, MOSQ_LOG_ERR, "Error whilst daemonising (%s): %s", "stdin", strerror(errno)); + exit(1); + } + if(!freopen("/dev/null", "w", stdout)){ + log__printf(NULL, MOSQ_LOG_ERR, "Error whilst daemonising (%s): %s", "stdout", strerror(errno)); + exit(1); + } + if(!freopen("/dev/null", "w", stderr)){ + log__printf(NULL, MOSQ_LOG_ERR, "Error whilst daemonising (%s): %s", "stderr", strerror(errno)); + exit(1); + } #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Can't start in daemon mode in Windows."); #endif From 0fb4e2bf6a87f3202e62ae19007adef2d2a454ba Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Thu, 22 Dec 2022 13:21:42 +0000 Subject: [PATCH 20/79] Fix incorrect topic-alias property value in mosquitto_sub json output. --- ChangeLog.txt | 4 ++++ client/sub_client_output.c | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 1140dd3b..aa01d5a4 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -6,6 +6,10 @@ Broker: - Fix std* files not being redirected when daemonising, when built with assertions removed. Closes #2708. +Clients: +- Fix incorrect topic-alias property value in mosquitto_sub json output. + + 2.0.15 - 2022-08-16 =================== diff --git a/client/sub_client_output.c b/client/sub_client_output.c index acefa167..8bf7cdb5 100644 --- a/client/sub_client_output.c +++ b/client/sub_client_output.c @@ -210,7 +210,7 @@ static int json_print_properties(cJSON *root, const mosquitto_property *properti break; case MQTT_PROP_TOPIC_ALIAS: - mosquitto_property_read_int16(prop, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, &i16value, false); + mosquitto_property_read_int16(prop, MQTT_PROP_TOPIC_ALIAS, &i16value, false); tmp = cJSON_CreateNumber(i16value); break; From 48f276f88dd881b7b30700cc289485d6df59c42e Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Fri, 30 Dec 2022 10:34:18 +0000 Subject: [PATCH 21/79] Fix multiple domains in renewal. --- misc/letsencrypt/mosquitto-copy.sh | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/misc/letsencrypt/mosquitto-copy.sh b/misc/letsencrypt/mosquitto-copy.sh index ef3d3766..3c19db7e 100755 --- a/misc/letsencrypt/mosquitto-copy.sh +++ b/misc/letsencrypt/mosquitto-copy.sh @@ -17,17 +17,19 @@ MY_DOMAIN=example.com # Set the directory that the certificates will be copied to. CERTIFICATE_DIR=/etc/mosquitto/certs -if [ "${RENEWED_DOMAINS}" = "${MY_DOMAIN}" ]; then - # Copy new certificate to Mosquitto directory - cp ${RENEWED_LINEAGE}/fullchain.pem ${CERTIFICATE_DIR}/server.pem - cp ${RENEWED_LINEAGE}/privkey.pem ${CERTIFICATE_DIR}/server.key +for D in ${RENEWED_DOMAINS}; do + if [ "${D}" = "${MY_DOMAIN}" ]; then + # Copy new certificate to Mosquitto directory + cp ${RENEWED_LINEAGE}/fullchain.pem ${CERTIFICATE_DIR}/server.pem + cp ${RENEWED_LINEAGE}/privkey.pem ${CERTIFICATE_DIR}/server.key - # Set ownership to Mosquitto - chown mosquitto: ${CERTIFICATE_DIR}/server.pem ${CERTIFICATE_DIR}/server.key + # Set ownership to Mosquitto + chown mosquitto: ${CERTIFICATE_DIR}/server.pem ${CERTIFICATE_DIR}/server.key - # Ensure permissions are restrictive - chmod 0600 ${CERTIFICATE_DIR}/server.pem ${CERTIFICATE_DIR}/server.key + # Ensure permissions are restrictive + chmod 0600 ${CERTIFICATE_DIR}/server.pem ${CERTIFICATE_DIR}/server.key - # Tell Mosquitto to reload certificates and configuration - pkill -HUP -x mosquitto -fi + # Tell Mosquitto to reload certificates and configuration + pkill -HUP -x mosquitto + fi +done From a6347cd7d26810238e7ce5c325fb5735f9178cfc Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Wed, 4 Jan 2023 11:38:28 +0000 Subject: [PATCH 22/79] Fix Coverity Scan 1501449. --- src/net.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/net.c b/src/net.c index b9a27685..e07da8b3 100644 --- a/src/net.c +++ b/src/net.c @@ -770,6 +770,7 @@ static int net__socket_listen_tcp(struct mosquitto__listener *listener) COMPAT_CLOSE(sock); listener->sock_count--; if(rc == MOSQ_ERR_NOT_FOUND || rc == MOSQ_ERR_INVAL){ + freeaddrinfo(ainfo); return rc; }else{ continue; From 4e6fbae45ce424d2204c8b5d51b37dc5a08013bc Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Sun, 15 Jan 2023 22:18:31 +0000 Subject: [PATCH 23/79] Delete old workflow runs. --- .github/workflows/delete-old-workflow-runs.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/delete-old-workflow-runs.yml diff --git a/.github/workflows/delete-old-workflow-runs.yml b/.github/workflows/delete-old-workflow-runs.yml new file mode 100644 index 00000000..29f9ca05 --- /dev/null +++ b/.github/workflows/delete-old-workflow-runs.yml @@ -0,0 +1,18 @@ +name: Delete old workflow runs +on: + workflow_dispatch: + schedule: + - cron: '0 0 1 * *' +# Run monthly, at 00:00 on the 1st day of month. + +jobs: + del_runs: + runs-on: ubuntu-latest + steps: + - name: Delete workflow runs + uses: Mattraks/delete-workflow-runs@v2 + with: + token: ${{ github.token }} + repository: ${{ github.repository }} + retain_days: 30 + keep_minimum_runs: 6 From e0d8ef20fa03ac238a67dc207c1d7854153f5590 Mon Sep 17 00:00:00 2001 From: Akos Vandra-Meyer Date: Fri, 20 Jan 2023 21:13:54 +0100 Subject: [PATCH 24/79] Fix malloc size for file path We need 4 more characters to store the suffix. --- plugins/dynamic-security/plugin.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/dynamic-security/plugin.c b/plugins/dynamic-security/plugin.c index 3ff1054a..980a3415 100644 --- a/plugins/dynamic-security/plugin.c +++ b/plugins/dynamic-security/plugin.c @@ -452,7 +452,7 @@ void dynsec__config_save(void) /* Save to file */ file_path_len = strlen(config_file) + 1; - file_path = mosquitto_malloc(file_path_len); + file_path = mosquitto_malloc(file_path_len + 4); if(file_path == NULL){ mosquitto_free(json_str); mosquitto_log_printf(MOSQ_LOG_ERR, "Error saving Dynamic security plugin config: Out of memory.\n"); From 19fbfee7914c959bdedb4ba23f599a3d86ec0e9a Mon Sep 17 00:00:00 2001 From: Hyeongon Kim Date: Mon, 6 Feb 2023 15:02:30 +0900 Subject: [PATCH 25/79] fixed a bug where broker's private-key file was not loaded fixed a bug where the mosquitto-broker's private-key file(.pem) dose not load with tls-engine. Signed-off-by: Hyeongon Kim --- src/net.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/net.c b/src/net.c index e07da8b3..0466a165 100644 --- a/src/net.c +++ b/src/net.c @@ -479,7 +479,7 @@ int net__load_certificates(struct mosquitto__listener *listener) net__print_ssl_error(NULL); return MOSQ_ERR_TLS; } - if(listener->tls_engine == NULL){ + if(listener->tls_engine == NULL || listener->tls_keyform == mosq_k_pem){ rc = SSL_CTX_use_PrivateKey_file(listener->ssl_ctx, listener->keyfile, SSL_FILETYPE_PEM); if(rc != 1){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load server key file \"%s\". Check keyfile.", listener->keyfile); From 2e2a420099f6869d11e23fb3f955e93b6547531a Mon Sep 17 00:00:00 2001 From: Tobias Assarsson Date: Thu, 2 Mar 2023 09:00:52 +0100 Subject: [PATCH 26/79] use CLOCK_BOOTTIME when available. Signed-off-by: Tobias Assarsson --- lib/mosquitto.c | 7 +++++-- lib/time_mosq.c | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/mosquitto.c b/lib/mosquitto.c index 27a44c15..76ba466a 100644 --- a/lib/mosquitto.c +++ b/lib/mosquitto.c @@ -61,8 +61,11 @@ int mosquitto_lib_init(void) srand((unsigned int)GetTickCount64()); #elif _POSIX_TIMERS>0 && defined(_POSIX_MONOTONIC_CLOCK) struct timespec tp; - - clock_gettime(CLOCK_MONOTONIC, &tp); +#ifdef CLOCK_BOOTTIME + clock_gettime(CLOCK_BOOTTIME, &tp); +#else + clock_gettime(CLOCK_MONOTONIC, &tp); +#endif srand((unsigned int)tp.tv_nsec); #elif defined(__APPLE__) uint64_t ticks; diff --git a/lib/time_mosq.c b/lib/time_mosq.c index 3a9362c2..1d2aeb1a 100644 --- a/lib/time_mosq.c +++ b/lib/time_mosq.c @@ -43,7 +43,11 @@ time_t mosquitto_time(void) #elif _POSIX_TIMERS>0 && defined(_POSIX_MONOTONIC_CLOCK) struct timespec tp; +#ifdef CLOCK_BOOTTIME + clock_gettime(CLOCK_BOOTTIME, &tp); +#else clock_gettime(CLOCK_MONOTONIC, &tp); +#endif return tp.tv_sec; #elif defined(__APPLE__) static mach_timebase_info_data_t tb; From 28a39e62ff7ad426b3bb01869ac4a8e3ab5ae50a Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 7 Mar 2023 14:16:27 +0000 Subject: [PATCH 27/79] Remove unused header. --- apps/db_dump/db_dump.c | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/db_dump/db_dump.c b/apps/db_dump/db_dump.c index 234e121c..7258d378 100644 --- a/apps/db_dump/db_dump.c +++ b/apps/db_dump/db_dump.c @@ -16,7 +16,6 @@ Contributors: Roger Light - initial implementation and documentation. */ -#include #include #include #include From 103253dedac32510ee965e74a0e56d5c1fe7648b Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 14 Mar 2023 23:16:49 +0000 Subject: [PATCH 28/79] Windows build for fixes branch. --- .github/workflows/windows.yml | 81 +++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 .github/workflows/windows.yml diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml new file mode 100644 index 00000000..b9a96fe7 --- /dev/null +++ b/.github/workflows/windows.yml @@ -0,0 +1,81 @@ +name: Windows build + +on: + workflow_dispatch: + push: + branches: [ "master", "fixes" ] + tags: [ "v[0-9]+.*" ] + pull_request: + branches: [ "master", "fixes" ] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + + cjson: + runs-on: windows-2022 + steps: + - uses: actions/checkout@v3 + with: + repository: DaveGamble/cJSON + ref: v1.7.15 + + - name: Configure CMake cJSON + run: cmake -B ${{github.workspace}}/build64 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_CJSON_TEST=OFF -DBUILD_SHARED_LIBS=OFF -DBUILD_SHARED_AND_STATIC_LIBS=OFF -DCJSON_BUILD_SHARED_LIBS=OFF -DCJSON_OVERRIDE_BUILD_SHARED_LIBS=OFF -DCMAKE_GENERATOR_PLATFORM=x64 + + - name: Build cJSON + run: cmake --build ${{github.workspace}}/build64 --config ${{env.BUILD_TYPE}} + + - name: Install cJSON + run: cmake --install ${{github.workspace}}/build64 --config ${{env.BUILD_TYPE}} + + - name: Upload cJSON + uses: actions/upload-artifact@v3 + with: + name: cjson-bin + path: C:\Program Files\cJSON + + mosquitto: + runs-on: windows-2022 + needs: + - cjson + env: + CJSON_DIR: C:\Program Files\cJSON + + steps: + - uses: actions/checkout@v3 + + - name: install openssl + run: choco install openssl + + - name: Download cJSON + uses: actions/download-artifact@v3 + with: + name: cjson-bin + path: C:\Program Files\cJSON + + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build64 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DWITH_TESTS=OFF -DCMAKE_GENERATOR_PLATFORM=x64 -DCJSON_INCLUDE_DIR="C:/Program Files/cJSON/include" -DCJSON_LIBRARY="C:/Program Files/cJSON/lib/cjson.lib" + + - name: Build + run: cmake --build ${{github.workspace}}/build64 --config ${{env.BUILD_TYPE}} + + - uses: suisei-cn/actions-download-file@v1.0.1 + id: vcredist + name: Download VC redistributable + with: + url: https://aka.ms/vs/17/release/vc_redist.x64.exe + target: ${{github.workspace}}/installer/ + + - name: Installer + uses: joncloud/makensis-action@v3.7 + with: + script-file: ${{github.workspace}}/installer/mosquitto.nsi + + - name: Upload installer to artifacts + uses: actions/upload-artifact/@v2 + with: + name: installer + path: ${{ github.workspace }}/installer/mosquitto*.exe From 7e35ac92ec6005fc6499f5592b110605bb311620 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Mon, 27 Mar 2023 23:42:19 +0100 Subject: [PATCH 29/79] Update changelog --- ChangeLog.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ChangeLog.txt b/ChangeLog.txt index aa01d5a4..1f4f69c4 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -6,6 +6,11 @@ Broker: - Fix std* files not being redirected when daemonising, when built with assertions removed. Closes #2708. +Client library: +- Use CLOCK_BOOTTIME when available, to keep track of time. This solves the + problem of the client OS sleeping and the client hence not being able to + calculate the actual time for keepalive purposes. Closes #2760. + Clients: - Fix incorrect topic-alias property value in mosquitto_sub json output. From 63da7472b519487611b047ca1d550e7dcc806286 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 28 Mar 2023 01:15:28 +0100 Subject: [PATCH 30/79] Fix default settings incorrectly allowing TLS v1.1. Closes #2722. Thanks to KramNamez. --- ChangeLog.txt | 2 ++ lib/net_mosq.c | 2 +- man/mosquitto.conf.5.xml | 2 +- src/net.c | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 1f4f69c4..2d19f52c 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -5,11 +5,13 @@ Broker: - Fix error handling related to the `bind_interface` option. - Fix std* files not being redirected when daemonising, when built with assertions removed. Closes #2708. +- Fix default settings incorrectly allowing TLS v1.1. Closes #2722. Client library: - Use CLOCK_BOOTTIME when available, to keep track of time. This solves the problem of the client OS sleeping and the client hence not being able to calculate the actual time for keepalive purposes. Closes #2760. +- Fix default settings incorrectly allowing TLS v1.1. Closes #2722. Clients: - Fix incorrect topic-alias property value in mosquitto_sub json output. diff --git a/lib/net_mosq.c b/lib/net_mosq.c index 80d9195b..b8d14a02 100644 --- a/lib/net_mosq.c +++ b/lib/net_mosq.c @@ -684,7 +684,7 @@ static int net__init_ssl_ctx(struct mosquitto *mosq) #endif if(!mosq->tls_version){ - SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1); + SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); #ifdef SSL_OP_NO_TLSv1_3 }else if(!strcmp(mosq->tls_version, "tlsv1.3")){ SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2); diff --git a/man/mosquitto.conf.5.xml b/man/mosquitto.conf.5.xml index 35e016c8..297dadb8 100644 --- a/man/mosquitto.conf.5.xml +++ b/man/mosquitto.conf.5.xml @@ -1458,7 +1458,7 @@ openssl dhparam -out dhparam.pem 2048 tlsv1.3, tlsv1.2 and tlsv1.1. If left unset, - the default of allowing TLS v1.3 and v1.2. + the default allows TLS v1.3 and v1.2. In Mosquitto version 1.6.x and earlier, this option set the only TLS protocol version that was allowed, rather than the minimum. diff --git a/src/net.c b/src/net.c index 0466a165..7ae33cbc 100644 --- a/src/net.c +++ b/src/net.c @@ -343,7 +343,7 @@ int net__tls_server_ctx(struct mosquitto__listener *listener) #endif if(listener->tls_version == NULL){ - SSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1); + SSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); #ifdef SSL_OP_NO_TLSv1_3 }else if(!strcmp(listener->tls_version, "tlsv1.3")){ SSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2); From 1195dd991e593e6593c173a1fa93e2e9dd045d3d Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Sat, 1 Apr 2023 08:50:03 +0100 Subject: [PATCH 31/79] Clearer len calculation. --- plugins/dynamic-security/plugin.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/dynamic-security/plugin.c b/plugins/dynamic-security/plugin.c index 980a3415..59aca8cd 100644 --- a/plugins/dynamic-security/plugin.c +++ b/plugins/dynamic-security/plugin.c @@ -451,8 +451,8 @@ void dynsec__config_save(void) json_str_len = strlen(json_str); /* Save to file */ - file_path_len = strlen(config_file) + 1; - file_path = mosquitto_malloc(file_path_len + 4); + file_path_len = strlen(config_file) + strlen(".new") + 1; + file_path = mosquitto_malloc(file_path_len); if(file_path == NULL){ mosquitto_free(json_str); mosquitto_log_printf(MOSQ_LOG_ERR, "Error saving Dynamic security plugin config: Out of memory.\n"); From 1bf753e773ff009258fa59a94c19cfcb90ec7dd9 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Sat, 1 Apr 2023 17:49:58 +0100 Subject: [PATCH 32/79] Fix confusing message on TLS certificate verification. Closes #2746. Thanks to Akos Vandra-Meyer. --- ChangeLog.txt | 1 + lib/packet_mosq.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/ChangeLog.txt b/ChangeLog.txt index 2d19f52c..58efc1a7 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -15,6 +15,7 @@ Client library: Clients: - Fix incorrect topic-alias property value in mosquitto_sub json output. +- Fix confusing message on TLS certificate verification. Closes #2746. 2.0.15 - 2022-08-16 diff --git a/lib/packet_mosq.c b/lib/packet_mosq.c index 80f47168..da2a6080 100644 --- a/lib/packet_mosq.c +++ b/lib/packet_mosq.c @@ -268,6 +268,8 @@ int packet__write(struct mosquitto *mosq) return MOSQ_ERR_CONN_LOST; case COMPAT_EINTR: return MOSQ_ERR_SUCCESS; + case EPROTO: + return MOSQ_ERR_TLS; default: return MOSQ_ERR_ERRNO; } From cc97f433c14ce1136307b266cd14a98703ee2018 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Sat, 1 Apr 2023 21:16:07 +0100 Subject: [PATCH 33/79] Use line buffered mode for stdout. Closes #2354. Closes #2749. --- ChangeLog.txt | 1 + src/logging.c | 3 +++ 2 files changed, 4 insertions(+) diff --git a/ChangeLog.txt b/ChangeLog.txt index 58efc1a7..11c0972b 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -6,6 +6,7 @@ Broker: - Fix std* files not being redirected when daemonising, when built with assertions removed. Closes #2708. - Fix default settings incorrectly allowing TLS v1.1. Closes #2722. +- Use line buffered mode for stdout. Closes #2354. Closes #2749. Client library: - Use CLOCK_BOOTTIME when available, to keep track of time. This solves the diff --git a/src/logging.c b/src/logging.c index f6361553..162a6762 100644 --- a/src/logging.c +++ b/src/logging.c @@ -130,6 +130,9 @@ int log__init(struct mosquitto__config *config) log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open log file %s for writing.", config->log_file); } } + if(log_destinations & MQTT3_LOG_STDOUT){ + setlinebuf(stdout); + } #ifdef WITH_DLT dlt_fifo_check(); if(dlt_allowed){ From 5a748806d360e58b87eb52890fe2192a7ce66898 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Sat, 1 Apr 2023 22:19:54 +0100 Subject: [PATCH 34/79] Default to max keepalive 0. --- src/conf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/conf.c b/src/conf.c index a3fbc7c4..a7b9b062 100644 --- a/src/conf.c +++ b/src/conf.c @@ -187,7 +187,7 @@ static void config__init_reload(struct mosquitto__config *config) config->log_timestamp = true; mosquitto__free(config->log_timestamp_format); config->log_timestamp_format = NULL; - config->max_keepalive = 65535; + config->max_keepalive = 0; config->max_packet_size = 0; config->max_inflight_messages = 20; config->max_queued_messages = 1000; From 655f9e58b79803fbb28b75b7a7cc23720f14f0cc Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Sat, 1 Apr 2023 22:28:02 +0100 Subject: [PATCH 35/79] Update blog post with non-expired Lets Encrypt root. Closes #2692. Thanks to Matt Turner. --- .../12/using-lets-encrypt-certificates-with-mosquitto.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/www/posts/2015/12/using-lets-encrypt-certificates-with-mosquitto.md b/www/posts/2015/12/using-lets-encrypt-certificates-with-mosquitto.md index 22eed9c9..069ecb90 100644 --- a/www/posts/2015/12/using-lets-encrypt-certificates-with-mosquitto.md +++ b/www/posts/2015/12/using-lets-encrypt-certificates-with-mosquitto.md @@ -17,13 +17,14 @@ Then use the following for your mosquitto.conf: ``` listener 8883 -cafile /etc/ssl/certs/DST_Root_CA_X3.pem +cafile /etc/ssl/certs/ISRG_Root_X1.pem certfile /etc/letsencrypt/live/example.com/fullchain.pem keyfile /etc/letsencrypt/live/example.com/privkey.pem ``` -You need to be aware that current versions of mosquitto never update listener -settings when running, so when you regenerate the server certificates you will -need to completely restart the broker. +Since version 2.0 of Mosquitto, you can send a SIGHUP to the broker to cause it +to reload certificates. Prior to this version, mosquitto would never update +listener settings when running, so you will need to completely restart the +broker. [Let's Encrypt]: https://letsencrypt.org/ From 4f9c5567ba67d301f369d75ff7580436103130ea Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Sat, 1 Apr 2023 22:35:51 +0100 Subject: [PATCH 36/79] Fix example plugin incorrect callback unregister. Closes #2673. Thanks to Karl Palsson. --- plugins/deny-protocol-version/mosquitto_deny_protocol_version.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/deny-protocol-version/mosquitto_deny_protocol_version.c b/plugins/deny-protocol-version/mosquitto_deny_protocol_version.c index 191cf710..323a0d81 100644 --- a/plugins/deny-protocol-version/mosquitto_deny_protocol_version.c +++ b/plugins/deny-protocol-version/mosquitto_deny_protocol_version.c @@ -106,5 +106,5 @@ int mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int op UNUSED(opts); UNUSED(opt_count); - return mosquitto_callback_unregister(mosq_pid, MOSQ_EVT_MESSAGE, basic_auth_callback, NULL); + return mosquitto_callback_unregister(mosq_pid, MOSQ_EVT_BASIC_AUTH, basic_auth_callback, NULL); } From 3b2f3c6d8a5b361c89e4fcc3ccebe126dc9a45f9 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Fri, 7 Apr 2023 19:29:13 +0100 Subject: [PATCH 37/79] Windows: Increase connection limit to 8192 where supported. Closes #2732. Thanks to zhousongsong. --- ChangeLog.txt | 2 ++ src/mosquitto.c | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 11c0972b..75a03b18 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -7,6 +7,8 @@ Broker: assertions removed. Closes #2708. - Fix default settings incorrectly allowing TLS v1.1. Closes #2722. - Use line buffered mode for stdout. Closes #2354. Closes #2749. +- Fix connections being limited to 2048 on Windows. The limit is now 8192, + where supported. Closes #2732. Client library: - Use CLOCK_BOOTTIME when available, to keep track of time. This solves the diff --git a/src/mosquitto.c b/src/mosquitto.c index 4dfd2bde..431254d8 100644 --- a/src/mosquitto.c +++ b/src/mosquitto.c @@ -484,7 +484,12 @@ int main(int argc, char *argv[]) #endif #ifdef WIN32 - _setmaxstdio(2048); + if(_setmaxstdio(8192) != 8192){ + /* Old limit was 2048 */ + if(_setmaxstdio(2048) != 2048){ + log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Unable to increase maximum allowed connections. This session may be limited to 512 connections."); + } + } #endif memset(&db, 0, sizeof(struct mosquitto_db)); From 6d240a9d183413fca7d2a4f42eae803ba84af97c Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Sat, 8 Apr 2023 09:07:56 +0100 Subject: [PATCH 38/79] Fix bridges being expired after being restored. This occurred with non-matching cleansession/local_cleansession being expired on start after restoring from persistence. Closes #2634. Thanks to everyone on the issue. --- ChangeLog.txt | 2 ++ src/persist_write.c | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 75a03b18..351eaad1 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -7,6 +7,8 @@ Broker: assertions removed. Closes #2708. - Fix default settings incorrectly allowing TLS v1.1. Closes #2722. - Use line buffered mode for stdout. Closes #2354. Closes #2749. +- Fix bridges with non-matching cleansession/local_cleansession being expired + on start after restoring from persistence. Closes #2634. - Fix connections being limited to 2048 on Windows. The limit is now 8192, where supported. Closes #2732. diff --git a/src/persist_write.c b/src/persist_write.c index fb3632fe..f0949f8a 100644 --- a/src/persist_write.c +++ b/src/persist_write.c @@ -167,11 +167,14 @@ static int persist__client_save(FILE *db_fptr) memset(&chunk, 0, sizeof(struct P_client)); HASH_ITER(hh_id, db.contexts_by_id, context, ctxt_tmp){ - if(context && (context->clean_start == false + if(context && #ifdef WITH_BRIDGE - || (context->bridge && context->bridge->clean_start_local == false) + ((!context->bridge && context->clean_start == false) + || (context->bridge && context->bridge->clean_start_local == false)) +#else + context->clean_start == false #endif - )){ + ){ chunk.F.session_expiry_time = context->session_expiry_time; if(context->session_expiry_interval != 0 && context->session_expiry_interval != UINT32_MAX && context->session_expiry_time == 0){ chunk.F.session_expiry_time = context->session_expiry_interval + db.now_real_s; From 3c518160097eac998a823e33d15df38093621863 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Wed, 12 Apr 2023 11:52:26 +0100 Subject: [PATCH 39/79] Fix leak on error. --- src/handle_connect.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/handle_connect.c b/src/handle_connect.c index 21405adf..7e102ee4 100644 --- a/src/handle_connect.c +++ b/src/handle_connect.c @@ -946,7 +946,13 @@ handle_connect_error: mosquitto__free(will_struct->msg.topic); mosquitto__free(will_struct); } - context->will = NULL; + if(context->will){ + mosquitto_property_free_all(&context->will->properties); + mosquitto__free(context->will->msg.payload); + mosquitto__free(context->will->msg.topic); + mosquitto__free(context->will); + context->will = NULL; + } #ifdef WITH_TLS if(client_cert) X509_free(client_cert); #endif From 269756a171a63a2ff96f1973df11b6563020201a Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Thu, 27 Apr 2023 23:25:52 +0100 Subject: [PATCH 40/79] Fix high CPU use on slow TLS connect. Closes #2794. Thanks to Evgeny S. --- ChangeLog.txt | 1 + lib/loop.c | 22 ++++++++++++---------- lib/mosquitto.c | 13 +------------ 3 files changed, 14 insertions(+), 22 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 351eaad1..655ddd07 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -17,6 +17,7 @@ Client library: problem of the client OS sleeping and the client hence not being able to calculate the actual time for keepalive purposes. Closes #2760. - Fix default settings incorrectly allowing TLS v1.1. Closes #2722. +- Fix high CPU use on slow TLS connect. Closes #2794. Clients: - Fix incorrect topic-alias property value in mosquitto_sub json output. diff --git a/lib/loop.c b/lib/loop.c index 965294f0..0277a1d3 100644 --- a/lib/loop.c +++ b/lib/loop.c @@ -63,20 +63,22 @@ int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets) if(mosq->sock != INVALID_SOCKET){ maxfd = mosq->sock; FD_SET(mosq->sock, &readfds); - pthread_mutex_lock(&mosq->current_out_packet_mutex); - pthread_mutex_lock(&mosq->out_packet_mutex); - if(mosq->out_packet || mosq->current_out_packet){ + if(mosq->want_write){ FD_SET(mosq->sock, &writefds); - } + }else{ #ifdef WITH_TLS - if(mosq->ssl){ - if(mosq->want_write){ - FD_SET(mosq->sock, &writefds); + if(mosq->ssl == NULL || SSL_is_init_finished(mosq->ssl)) +#endif + { + pthread_mutex_lock(&mosq->current_out_packet_mutex); + pthread_mutex_lock(&mosq->out_packet_mutex); + if(mosq->out_packet || mosq->current_out_packet){ + FD_SET(mosq->sock, &writefds); + } + pthread_mutex_unlock(&mosq->out_packet_mutex); + pthread_mutex_unlock(&mosq->current_out_packet_mutex); } } -#endif - pthread_mutex_unlock(&mosq->out_packet_mutex); - pthread_mutex_unlock(&mosq->current_out_packet_mutex); }else{ #ifdef WITH_SRV if(mosq->achan){ diff --git a/lib/mosquitto.c b/lib/mosquitto.c index 83bf42c2..a83d43a7 100644 --- a/lib/mosquitto.c +++ b/lib/mosquitto.c @@ -332,18 +332,7 @@ int mosquitto_socket(struct mosquitto *mosq) bool mosquitto_want_write(struct mosquitto *mosq) { - bool result = false; - if(mosq->out_packet || mosq->current_out_packet){ - result = true; - } -#ifdef WITH_TLS - if(mosq->ssl){ - if (mosq->want_write) { - result = true; - } - } -#endif - return result; + return mosq->out_packet || mosq->current_out_packet || mosq->want_write; } From 65a6ed43a5a4806dc1ea12f9a52ed57ad6de1c5a Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Thu, 11 May 2023 23:59:36 +0100 Subject: [PATCH 41/79] Docker: Bump alpine to 3.18. --- docker/1.5-openssl/Dockerfile | 9 +++++++-- docker/2.0-openssl/Dockerfile | 2 +- docker/2.0/Dockerfile | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/docker/1.5-openssl/Dockerfile b/docker/1.5-openssl/Dockerfile index 2d258cbd..69ade540 100644 --- a/docker/1.5-openssl/Dockerfile +++ b/docker/1.5-openssl/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.14 +FROM alpine:3.18 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" @@ -6,16 +6,19 @@ LABEL maintainer="Roger Light " \ ENV VERSION=1.5.11 \ DOWNLOAD_SHA256=4a3b8a8f5505d27a7a966dd68bfd76f1e69feb51796d1b46b7271d1bb5a1a299 \ GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7 \ - LWS_VERSION=2.4.2 + LWS_VERSION=4.2.1 \ + LWS_SHA256=842da21f73ccba2be59e680de10a8cce7928313048750eb6ad73b6fa50763c51 RUN set -x && \ apk --no-cache add --virtual build-deps \ build-base \ cmake \ gnupg \ + linux-headers \ openssl-dev \ util-linux-dev && \ wget https://github.com/warmcat/libwebsockets/archive/v${LWS_VERSION}.tar.gz -O /tmp/lws.tar.gz && \ + echo "$LWS_SHA256 /tmp/lws.tar.gz" | sha256sum -c - && \ mkdir -p /build/lws && \ tar --strip=1 -xf /tmp/lws.tar.gz -C /build/lws && \ rm /tmp/lws.tar.gz && \ @@ -23,11 +26,13 @@ RUN set -x && \ cmake . \ -DCMAKE_BUILD_TYPE=MinSizeRel \ -DCMAKE_INSTALL_PREFIX=/usr \ + -DDISABLE_WERROR=ON \ -DLWS_IPV6=ON \ -DLWS_WITHOUT_BUILTIN_GETIFADDRS=ON \ -DLWS_WITHOUT_CLIENT=ON \ -DLWS_WITHOUT_EXTENSIONS=ON \ -DLWS_WITHOUT_TESTAPPS=ON \ + -DLWS_WITH_EXTERNAL_POLL=ON \ -DLWS_WITH_HTTP2=OFF \ -DLWS_WITH_SHARED=OFF \ -DLWS_WITH_ZIP_FOPS=OFF \ diff --git a/docker/2.0-openssl/Dockerfile b/docker/2.0-openssl/Dockerfile index d0eb1ebb..1cd4c96c 100644 --- a/docker/2.0-openssl/Dockerfile +++ b/docker/2.0-openssl/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.16 +FROM alpine:3.18 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" diff --git a/docker/2.0/Dockerfile b/docker/2.0/Dockerfile index a1056a4c..d58983ed 100644 --- a/docker/2.0/Dockerfile +++ b/docker/2.0/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.16 +FROM alpine:3.18 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" From cc1aac336ecbbc251ed4a5126257f6f4fe3ed27c Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Fri, 12 May 2023 23:17:10 +0100 Subject: [PATCH 42/79] Bump remaining official docker images to Alpine 3.18 --- docker/1.6-openssl/Dockerfile | 2 +- docker/generic/Dockerfile | 2 +- docker/local/Dockerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/1.6-openssl/Dockerfile b/docker/1.6-openssl/Dockerfile index 025d85f1..8c98d8ad 100644 --- a/docker/1.6-openssl/Dockerfile +++ b/docker/1.6-openssl/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.14 +FROM alpine:3.18 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" diff --git a/docker/generic/Dockerfile b/docker/generic/Dockerfile index 656f8a15..b7970bfb 100644 --- a/docker/generic/Dockerfile +++ b/docker/generic/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.14 +FROM alpine:3.18 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" diff --git a/docker/local/Dockerfile b/docker/local/Dockerfile index 72b73359..475ae687 100644 --- a/docker/local/Dockerfile +++ b/docker/local/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.14 +FROM alpine:3.18 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" From 105e30ff5bedd8d4ffe8bf0cc618378df7ca966f Mon Sep 17 00:00:00 2001 From: Bart Desplanques Date: Sat, 27 May 2023 01:07:55 +0200 Subject: [PATCH 43/79] Fix engine support. Do not try to open keyfile when keyform is "engine": this is not necessarily a real file. Dynamic engines require init with OPENSSL_INIT_ENGINE_DYNAMIC before they can be loaded. Signed-off-by: Bart Desplanques --- apps/mosquitto_ctrl/options.c | 10 +++++----- client/client_shared.c | 10 +++++----- lib/options.c | 29 ++++++++++++++++++----------- man/mosquitto.conf.5.xml | 9 ++++++--- 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/apps/mosquitto_ctrl/options.c b/apps/mosquitto_ctrl/options.c index 592d9e8c..ff32eafb 100644 --- a/apps/mosquitto_ctrl/options.c +++ b/apps/mosquitto_ctrl/options.c @@ -593,6 +593,11 @@ int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg) return 1; } #ifdef WITH_TLS + if(cfg->keyform && mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, cfg->keyform)){ + fprintf(stderr, "Error: Problem setting key form, it must be one of 'pem' or 'engine'.\n"); + mosquitto_lib_cleanup(); + return 1; + } if(cfg->cafile || cfg->capath){ rc = mosquitto_tls_set(mosq, cfg->cafile, cfg->capath, cfg->certfile, cfg->keyfile, NULL); if(rc){ @@ -615,11 +620,6 @@ int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg) mosquitto_lib_cleanup(); return 1; } - if(cfg->keyform && mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, cfg->keyform)){ - fprintf(stderr, "Error: Problem setting key form, it must be one of 'pem' or 'engine'.\n"); - mosquitto_lib_cleanup(); - return 1; - } if(cfg->tls_engine_kpass_sha1 && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE_KPASS_SHA1, cfg->tls_engine_kpass_sha1)){ fprintf(stderr, "Error: Problem setting TLS engine key pass sha, is it a 40 character hex string?\n"); mosquitto_lib_cleanup(); diff --git a/client/client_shared.c b/client/client_shared.c index fee029a6..0dd7de44 100644 --- a/client/client_shared.c +++ b/client/client_shared.c @@ -1253,6 +1253,11 @@ int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg) return 1; } #ifdef WITH_TLS + if(cfg->keyform && mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, cfg->keyform)){ + err_printf(cfg, "Error: Problem setting key form, it must be one of 'pem' or 'engine'.\n"); + mosquitto_lib_cleanup(); + return 1; + } if(cfg->cafile || cfg->capath){ rc = mosquitto_tls_set(mosq, cfg->cafile, cfg->capath, cfg->certfile, cfg->keyfile, NULL); if(rc){ @@ -1289,11 +1294,6 @@ int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg) mosquitto_lib_cleanup(); return 1; } - if(cfg->keyform && mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, cfg->keyform)){ - err_printf(cfg, "Error: Problem setting key form, it must be one of 'pem' or 'engine'.\n"); - mosquitto_lib_cleanup(); - return 1; - } if(cfg->tls_engine_kpass_sha1 && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE_KPASS_SHA1, cfg->tls_engine_kpass_sha1)){ err_printf(cfg, "Error: Problem setting TLS engine key pass sha, is it a 40 character hex string?\n"); mosquitto_lib_cleanup(); diff --git a/lib/options.c b/lib/options.c index 23c88352..bf102112 100644 --- a/lib/options.c +++ b/lib/options.c @@ -179,19 +179,21 @@ int mosquitto_tls_set(struct mosquitto *mosq, const char *cafile, const char *ca mosquitto__free(mosq->tls_keyfile); mosq->tls_keyfile = NULL; if(keyfile){ - fptr = mosquitto__fopen(keyfile, "rt", false); - if(fptr){ - fclose(fptr); - }else{ - mosquitto__free(mosq->tls_cafile); - mosq->tls_cafile = NULL; + if(mosq->tls_keyform == mosq_k_pem){ + fptr = mosquitto__fopen(keyfile, "rt", false); + if(fptr){ + fclose(fptr); + }else{ + mosquitto__free(mosq->tls_cafile); + mosq->tls_cafile = NULL; - mosquitto__free(mosq->tls_capath); - mosq->tls_capath = NULL; + mosquitto__free(mosq->tls_capath); + mosq->tls_capath = NULL; - mosquitto__free(mosq->tls_certfile); - mosq->tls_certfile = NULL; - return MOSQ_ERR_INVAL; + mosquitto__free(mosq->tls_certfile); + mosq->tls_certfile = NULL; + return MOSQ_ERR_INVAL; + } } mosq->tls_keyfile = mosquitto__strdup(keyfile); if(!mosq->tls_keyfile){ @@ -290,6 +292,11 @@ int mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt_t option, cons #if defined(WITH_TLS) && !defined(OPENSSL_NO_ENGINE) mosquitto__free(mosq->tls_engine); if(value){ +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + /* The "Dynamic" OpenSSL engine is not initialized by default but + is required by ENGINE_by_id() to find dynamically loadable engines */ + OPENSSL_init_crypto(OPENSSL_INIT_ENGINE_DYNAMIC, NULL); +#endif eng = ENGINE_by_id(value); if(!eng){ return MOSQ_ERR_INVAL; diff --git a/man/mosquitto.conf.5.xml b/man/mosquitto.conf.5.xml index 297dadb8..90509fd4 100644 --- a/man/mosquitto.conf.5.xml +++ b/man/mosquitto.conf.5.xml @@ -1391,9 +1391,12 @@ openssl dhparam -out dhparam.pem 2048 file path - Path to the PEM encoded server key. This - option and must be present - to enable certificate based TLS encryption. + If equals "pem" this is the + path to the PEM encoded server key. This option + and must be present + to enable certificate based TLS encryption. If + is "engine" this represents + the engine handle of the private key. The private key pointed to by this option will be From 18ea97c468047da56bbf3eea5146130a87163254 Mon Sep 17 00:00:00 2001 From: linkkzz Date: Fri, 30 Jun 2023 11:29:40 +0800 Subject: [PATCH 44/79] Fixes sub_count is not decreased when client ubsubscribe Signed-off-by: linkkzz --- src/subs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/subs.c b/src/subs.c index 687537d9..c5c6b5a3 100644 --- a/src/subs.c +++ b/src/subs.c @@ -389,6 +389,7 @@ static int sub__remove_normal(struct mosquitto *context, struct mosquitto__subhi if(context->subs[i] && context->subs[i]->hier == subhier){ mosquitto__free(context->subs[i]); context->subs[i] = NULL; + context->sub_count--; break; } } @@ -429,6 +430,7 @@ static int sub__remove_shared(struct mosquitto *context, struct mosquitto__subhi mosquitto__free(context->subs[i]); context->subs[i] = NULL; + context->sub_count--; break; } } From c749ed6fd58c77e36319ba0adcb84d31e8edab50 Mon Sep 17 00:00:00 2001 From: Norbert Heusser Date: Mon, 3 Jul 2023 11:07:31 +0000 Subject: [PATCH 45/79] Do not add outgoing bridge connection context to session expiry list on disconnect Signed-off-by: Norbert Heusser --- src/context.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/context.c b/src/context.c index b2f851ea..923d06f8 100644 --- a/src/context.c +++ b/src/context.c @@ -214,19 +214,20 @@ void context__disconnect(struct mosquitto *context) context__send_will(context); net__socket_close(context); - if(context->session_expiry_interval == 0){ - /* Client session is due to be expired now */ #ifdef WITH_BRIDGE - if(context->bridge == NULL) + if(context->bridge == NULL) + /* Outgoing bridge connection never expire */ #endif - { + { + if(context->session_expiry_interval == 0){ + /* Client session is due to be expired now */ if(context->will_delay_interval == 0){ /* This will be done later, after the will is published for delay>0. */ context__add_to_disused(context); } + }else{ + session_expiry__add(context); } - }else{ - session_expiry__add(context); } keepalive__remove(context); mosquitto__set_state(context, mosq_cs_disconnected); From 41a3a2068ee014ddfb7f07108ad5e6d4e718e329 Mon Sep 17 00:00:00 2001 From: Philippe Coval Date: Sat, 8 Jul 2023 11:47:24 +0200 Subject: [PATCH 46/79] git: Ignore more generated files Signed-off-by: Philippe Coval --- .gitignore | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/.gitignore b/.gitignore index 5694e123..b5bb1697 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +# .gitignore *.db *.gcda *.gcno @@ -85,3 +86,20 @@ test/unit/out/ www/cache/ __pycache__ + +# Debian generated files +debian/.debhelper/ +debian/debhelper-build-stamp +debian/files +debian/*.log +debian/*.substvars +debian/*mosquitto*/ +debian/*.debhelper +debian/tmp/ +obj-*/ + +# Emacs generated files +*~ + +# Others +tmp/ From e653f75f67809473d1d5a3c0de408123058891f8 Mon Sep 17 00:00:00 2001 From: Philippe Coval Date: Sun, 9 Jul 2023 14:58:02 +0200 Subject: [PATCH 47/79] man: Fix typo-in-manual-page noticable noticeable This was reported by debian lintian checker Forwarded: https://github.com/eclipse/mosquitto/pull/2846#Open Signed-off-by: Philippe Coval --- man/mosquitto_ctrl.1.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/man/mosquitto_ctrl.1.xml b/man/mosquitto_ctrl.1.xml index 053a0bce..a96d8143 100644 --- a/man/mosquitto_ctrl.1.xml +++ b/man/mosquitto_ctrl.1.xml @@ -319,7 +319,7 @@ Disable Nagle's algorithm for the socket. This means that latency of sent messages is reduced, which is - particularly noticable for small, reasonably infrequent + particularly noticeable for small, reasonably infrequent messages. Using this option may result in more packets being sent than would normally be necessary. From cb90e911267b0c49b5b693a99b1b573c18b1b03b Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Fri, 11 Aug 2023 23:26:46 +0100 Subject: [PATCH 48/79] Add missing mosquitto_strerror entries. Closes #2799. Thanks to n00b42 --- lib/strings_mosq.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/strings_mosq.c b/lib/strings_mosq.c index 419294a6..2516b06b 100644 --- a/lib/strings_mosq.c +++ b/lib/strings_mosq.c @@ -75,6 +75,10 @@ const char *mosquitto_strerror(int mosq_errno) return "Proxy error."; case MOSQ_ERR_MALFORMED_UTF8: return "Malformed UTF-8"; + case MOSQ_ERR_KEEPALIVE: + return "Keepalive exceeded"; + case MOSQ_ERR_LOOKUP: + return "DNS Lookup failed"; case MOSQ_ERR_DUPLICATE_PROPERTY: return "Duplicate property in property list"; case MOSQ_ERR_TLS_HANDSHAKE: From 4ca294fd9c2d460deb15ae85ccd891178df05704 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Thu, 8 Jun 2023 21:02:29 +0100 Subject: [PATCH 49/79] Warn on lax permissions on sensitive files. - Broker will log warnings if sensitive files are world readable/writable, or if the owner/group is not the same as the user/group the broker is running as. In future versions the broker will refuse to open these files. --- ChangeLog.txt | 3 + apps/mosquitto_ctrl/CMakeLists.txt | 1 + apps/mosquitto_ctrl/Makefile | 1 + apps/mosquitto_ctrl/dynsec.c | 5 +- apps/mosquitto_passwd/mosquitto_passwd.c | 6 +- lib/misc_mosq.c | 56 +++++++ plugins/dynamic-security/plugin.c | 188 ++++++++++++++++++++++- src/net.c | 3 +- src/persist_read.c | 2 +- src/security_default.c | 4 +- 10 files changed, 258 insertions(+), 11 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 655ddd07..8e619c0c 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -11,6 +11,9 @@ Broker: on start after restoring from persistence. Closes #2634. - Fix connections being limited to 2048 on Windows. The limit is now 8192, where supported. Closes #2732. +- Broker will log warnings if sensitive files are world readable/writable, or + if the owner/group is not the same as the user/group the broker is running + as. In future versions the broker will refuse to open these files. Client library: - Use CLOCK_BOOTTIME when available, to keep track of time. This solves the diff --git a/apps/mosquitto_ctrl/CMakeLists.txt b/apps/mosquitto_ctrl/CMakeLists.txt index d4466fe5..26dbe8d6 100644 --- a/apps/mosquitto_ctrl/CMakeLists.txt +++ b/apps/mosquitto_ctrl/CMakeLists.txt @@ -17,6 +17,7 @@ if (WITH_TLS AND CJSON_FOUND) dynsec_role.c ../mosquitto_passwd/get_password.c ../mosquitto_passwd/get_password.h ../../lib/memory_mosq.c ../../lib/memory_mosq.h + ../../lib/misc_mosq.c ../../lib/misc_mosq.h ../../src/memory_public.c options.c ../../src/password_mosq.c ../../src/password_mosq.h diff --git a/apps/mosquitto_ctrl/Makefile b/apps/mosquitto_ctrl/Makefile index 502f0dac..cf9ac0dc 100644 --- a/apps/mosquitto_ctrl/Makefile +++ b/apps/mosquitto_ctrl/Makefile @@ -23,6 +23,7 @@ OBJS= mosquitto_ctrl.o \ get_password.o \ memory_mosq.o \ memory_public.o \ + misc_mosq.o \ options.o \ password_mosq.o diff --git a/apps/mosquitto_ctrl/dynsec.c b/apps/mosquitto_ctrl/dynsec.c index c74147b6..8e5dd726 100644 --- a/apps/mosquitto_ctrl/dynsec.c +++ b/apps/mosquitto_ctrl/dynsec.c @@ -30,6 +30,7 @@ Contributors: #include "mosquitto.h" #include "password_mosq.h" #include "get_password.h" +#include "misc_mosq.h" void dynsec__print_usage(void) { @@ -738,7 +739,7 @@ static int dynsec_init(int argc, char *argv[]) admin_password = password; } - fptr = fopen(filename, "rb"); + fptr = mosquitto__fopen(filename, "rb", true); if(fptr){ fclose(fptr); fprintf(stderr, "dynsec init: '%s' already exists. Remove the file or use a different location..\n", filename); @@ -753,7 +754,7 @@ static int dynsec_init(int argc, char *argv[]) json_str = cJSON_Print(tree); cJSON_Delete(tree); - fptr = fopen(filename, "wb"); + fptr = mosquitto__fopen(filename, "wb", true); if(fptr){ fprintf(fptr, "%s", json_str); free(json_str); diff --git a/apps/mosquitto_passwd/mosquitto_passwd.c b/apps/mosquitto_passwd/mosquitto_passwd.c index f5b2470e..0a5ba722 100644 --- a/apps/mosquitto_passwd/mosquitto_passwd.c +++ b/apps/mosquitto_passwd/mosquitto_passwd.c @@ -374,7 +374,7 @@ static int create_backup(const char *backup_file, FILE *fptr) { FILE *fbackup; - fbackup = fopen(backup_file, "wt"); + fbackup = mosquitto__fopen(backup_file, "wt", true); if(!fbackup){ fprintf(stderr, "Error creating backup password file \"%s\", not continuing.\n", backup_file); return 1; @@ -599,7 +599,7 @@ int main(int argc, char *argv[]) } password_cmd = password; } - fptr = fopen(password_file, "wt"); + fptr = mosquitto__fopen(password_file, "wt", true); if(!fptr){ fprintf(stderr, "Error: Unable to open file %s for writing. %s.\n", password_file, strerror(errno)); free(password_file); @@ -610,7 +610,7 @@ int main(int argc, char *argv[]) fclose(fptr); return rc; }else{ - fptr = fopen(password_file, "r+t"); + fptr = mosquitto__fopen(password_file, "r+t", true); if(!fptr){ fprintf(stderr, "Error: Unable to open password file %s. %s.\n", password_file, strerror(errno)); free(password_file); diff --git a/lib/misc_mosq.c b/lib/misc_mosq.c index 2529d794..3ee6fc79 100644 --- a/lib/misc_mosq.c +++ b/lib/misc_mosq.c @@ -38,6 +38,8 @@ Contributors: # define PATH_MAX MAX_PATH #else # include +# include +# include # include #endif @@ -149,6 +151,60 @@ FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read) return NULL; } + if(restrict_read){ + if(statbuf.st_mode & S_IRWXO){ +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_WARNING, +#else + fprintf(stderr, +#endif + "Warning: File %s has world readable permissions. Future versions will refuse to load this file.", + path); +#if 0 + return NULL; +#endif + } + if(statbuf.st_uid != getuid()){ + char buf[4096]; + struct passwd pw, *result; + + getpwuid_r(getuid(), &pw, buf, sizeof(buf), &result); + if(result){ +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_WARNING, +#else + fprintf(stderr, +#endif + "Warning: File %s owner is not %s. Future versions will refuse to load this file.", + path, result->pw_name); + } +#if 0 + // Future version + return NULL; +#endif + } + if(statbuf.st_gid != getgid()){ + char buf[4096]; + struct group grp, *result; + + getgrgid_r(getgid(), &grp, buf, sizeof(buf), &result); + if(result){ +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_WARNING, +#else + fprintf(stderr, +#endif + "Warning: File %s group is not %s. Future versions will refuse to load this file.", + path, result->gr_name); + } +#if 0 + // Future version + return NULL +#endif + } + } + + if(!S_ISREG(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)){ #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_ERR, "Error: %s is not a file.", path); diff --git a/plugins/dynamic-security/plugin.c b/plugins/dynamic-security/plugin.c index 59aca8cd..7a1119c7 100644 --- a/plugins/dynamic-security/plugin.c +++ b/plugins/dynamic-security/plugin.c @@ -41,6 +41,190 @@ static mosquitto_plugin_id_t *plg_id = NULL; static char *config_file = NULL; struct dynsec__acl_default_access default_access = {false, false, false, false}; +#ifdef WIN32 +# include +# include +# include +# include +# include +# define PATH_MAX MAX_PATH +#else +# include +# include +# include +# include +#endif +/* Temporary - remove in 2.1 */ +FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read) +{ +#ifdef WIN32 + char buf[4096]; + int rc; + int flags = 0; + + rc = ExpandEnvironmentStringsA(path, buf, 4096); + if(rc == 0 || rc > 4096){ + return NULL; + }else{ + if (restrict_read) { + HANDLE hfile; + SECURITY_ATTRIBUTES sec; + EXPLICIT_ACCESS_A ea; + PACL pacl = NULL; + char username[UNLEN + 1]; + DWORD ulen = UNLEN; + SECURITY_DESCRIPTOR sd; + DWORD dwCreationDisposition; + int fd; + FILE *fptr; + + switch(mode[0]){ + case 'a': + dwCreationDisposition = OPEN_ALWAYS; + flags = _O_APPEND; + break; + case 'r': + dwCreationDisposition = OPEN_EXISTING; + flags = _O_RDONLY; + break; + case 'w': + dwCreationDisposition = CREATE_ALWAYS; + break; + default: + return NULL; + } + + GetUserNameA(username, &ulen); + if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { + return NULL; + } + BuildExplicitAccessWithNameA(&ea, username, GENERIC_ALL, SET_ACCESS, NO_INHERITANCE); + if (SetEntriesInAclA(1, &ea, NULL, &pacl) != ERROR_SUCCESS) { + return NULL; + } + if (!SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE)) { + LocalFree(pacl); + return NULL; + } + + memset(&sec, 0, sizeof(sec)); + sec.nLength = sizeof(SECURITY_ATTRIBUTES); + sec.bInheritHandle = FALSE; + sec.lpSecurityDescriptor = &sd; + + hfile = CreateFileA(buf, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, + &sec, + dwCreationDisposition, + FILE_ATTRIBUTE_NORMAL, + NULL); + + LocalFree(pacl); + + fd = _open_osfhandle((intptr_t)hfile, flags); + if (fd < 0) { + return NULL; + } + + fptr = _fdopen(fd, mode); + if (!fptr) { + _close(fd); + return NULL; + } + if(mode[0] == 'a'){ + fseek(fptr, 0, SEEK_END); + } + return fptr; + + }else { + return fopen(buf, mode); + } + } +#else + FILE *fptr; + struct stat statbuf; + + if (restrict_read) { + mode_t old_mask; + + old_mask = umask(0077); + fptr = fopen(path, mode); + umask(old_mask); + }else{ + fptr = fopen(path, mode); + } + if(!fptr) return NULL; + + if(fstat(fileno(fptr), &statbuf) < 0){ + fclose(fptr); + return NULL; + } + + if(restrict_read){ + if(statbuf.st_mode & S_IRWXO){ +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_WARNING, +#else + fprintf(stderr, +#endif + "Warning: File %s has world readable permissions. Future versions will refuse to load this file.", + path); +#if 0 + return NULL; +#endif + } + if(statbuf.st_uid != getuid()){ + char buf[4096]; + struct passwd pw, *result; + + getpwuid_r(getuid(), &pw, buf, sizeof(buf), &result); + if(result){ +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_WARNING, +#else + fprintf(stderr, +#endif + "Warning: File %s owner is not %s. Future versions will refuse to load this file.", + path, result->pw_name); + } +#if 0 + // Future version + return NULL; +#endif + } + if(statbuf.st_gid != getgid()){ + char buf[4096]; + struct group grp, *result; + + getgrgid_r(getgid(), &grp, buf, sizeof(buf), &result); + if(result){ +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_WARNING, +#else + fprintf(stderr, +#endif + "Warning: File %s group is not %s. Future versions will refuse to load this file.", + path, result->gr_name); + } +#if 0 + // Future version + return NULL +#endif + } + } + + + if(!S_ISREG(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)){ +#ifdef WITH_BROKER + log__printf(NULL, MOSQ_LOG_ERR, "Error: %s is not a file.", path); +#endif + fclose(fptr); + return NULL; + } + return fptr; +#endif +} + + void dynsec__command_reply(cJSON *j_responses, struct mosquitto *context, const char *command, const char *error, const char *correlation_data) { cJSON *j_response; @@ -359,7 +543,7 @@ static int dynsec__config_load(void) /* Load from file */ errno = 0; - fptr = fopen(config_file, "rb"); + fptr = mosquitto__fopen(config_file, "rb", true); if(fptr == NULL){ mosquitto_log_printf(MOSQ_LOG_ERR, "Error loading Dynamic security plugin config: File is not readable - check permissions.\n"); return MOSQ_ERR_ERRNO; @@ -460,7 +644,7 @@ void dynsec__config_save(void) } snprintf(file_path, file_path_len, "%s.new", config_file); - fptr = fopen(file_path, "wt"); + fptr = mosquitto__fopen(file_path, "wt", true); if(fptr == NULL){ mosquitto_free(json_str); mosquitto_free(file_path); diff --git a/src/net.c b/src/net.c index 7ae33cbc..51452307 100644 --- a/src/net.c +++ b/src/net.c @@ -56,6 +56,7 @@ Contributors: #include "mosquitto_broker_internal.h" #include "mqtt_protocol.h" #include "memory_mosq.h" +#include "misc_mosq.h" #include "net_mosq.h" #include "util_mosq.h" @@ -416,7 +417,7 @@ int net__tls_server_ctx(struct mosquitto__listener *listener) #endif if(listener->dhparamfile){ - dhparamfile = fopen(listener->dhparamfile, "r"); + dhparamfile = mosquitto__fopen(listener->dhparamfile, "r", true); if(!dhparamfile){ log__printf(NULL, MOSQ_LOG_ERR, "Error loading dhparamfile \"%s\".", listener->dhparamfile); return MOSQ_ERR_TLS; diff --git a/src/persist_read.c b/src/persist_read.c index 107e8602..14a2f4f0 100644 --- a/src/persist_read.c +++ b/src/persist_read.c @@ -427,7 +427,7 @@ int persist__restore(void) db.msg_store_load = NULL; - fptr = mosquitto__fopen(db.config->persistence_filepath, "rb", false); + fptr = mosquitto__fopen(db.config->persistence_filepath, "rb", true); if(fptr == NULL) return MOSQ_ERR_SUCCESS; rlen = fread(&header, 1, 15, fptr); if(rlen == 0){ diff --git a/src/security_default.c b/src/security_default.c index 4fc6a78b..015ce01b 100644 --- a/src/security_default.c +++ b/src/security_default.c @@ -530,7 +530,7 @@ static int aclfile__parse(struct mosquitto__security_options *security_opts) return MOSQ_ERR_NOMEM; } - aclfptr = mosquitto__fopen(security_opts->acl_file, "rt", false); + aclfptr = mosquitto__fopen(security_opts->acl_file, "rt", true); if(!aclfptr){ mosquitto__free(buf); log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open acl_file \"%s\".", security_opts->acl_file); @@ -755,7 +755,7 @@ static int pwfile__parse(const char *file, struct mosquitto__unpwd **root) return MOSQ_ERR_NOMEM; } - pwfile = mosquitto__fopen(file, "rt", false); + pwfile = mosquitto__fopen(file, "rt", true); if(!pwfile){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open pwfile \"%s\".", file); mosquitto__free(buf); From 9bb805857899079be986e5af026ae8c68b29d120 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Thu, 8 Jun 2023 22:13:53 +0100 Subject: [PATCH 50/79] Reject Will messages that attempt to publish to $CONTROL/. --- ChangeLog.txt | 3 ++ src/handle_connect.c | 4 +++ test/broker/07-will-control.py | 51 ++++++++++++++++++++++++++++++++++ test/broker/Makefile | 1 + test/broker/test.py | 1 + 5 files changed, 60 insertions(+) create mode 100755 test/broker/07-will-control.py diff --git a/ChangeLog.txt b/ChangeLog.txt index 8e619c0c..e382b41c 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,6 @@ +Security: +- Broker will reject Will messages that attempt to publish to $CONTROL/. + Broker: - Fix $SYS messages being expired after 60 seconds and hence unchanged values disappearing. diff --git a/src/handle_connect.c b/src/handle_connect.c index 7e102ee4..63824b0c 100644 --- a/src/handle_connect.c +++ b/src/handle_connect.c @@ -375,6 +375,10 @@ static int will__read(struct mosquitto *context, const char *client_id, struct m will_struct->msg.topic = will_topic_mount; } + if(!strncmp(will_struct->msg.topic, "$CONTROL/", strlen("$CONTROL/"))){ + rc = MOSQ_ERR_ACL_DENIED; + goto error_cleanup; + } rc = mosquitto_pub_topic_check(will_struct->msg.topic); if(rc) goto error_cleanup; diff --git a/test/broker/07-will-control.py b/test/broker/07-will-control.py new file mode 100755 index 00000000..68d2eccb --- /dev/null +++ b/test/broker/07-will-control.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 + +# Test whether a client setting a will with $CONTROL in is denied + +from mosq_test_helper import * + + +def do_test(start_broker, proto_ver): + rc = 1 + mid = 1 + connect_packet = mosq_test.gen_connect("will", will_topic="$CONTROL/dynamic-security/v1", will_payload=b"will-message", proto_ver=proto_ver) + + port = mosq_test.get_port() + if start_broker: + broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) + + try: + sock = mosq_test.client_connect_only(port=port) + sock.send(connect_packet) + d = sock.recv(1) + if d == b"": + rc = 0 + + sock.close() + except mosq_test.TestError: + pass + except Exception as e: + print(e) + finally: + if start_broker: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde.decode('utf-8')) + exit(rc) + else: + return rc + + +def all_tests(start_broker=False): + rc = do_test(start_broker, proto_ver=4) + if rc: + return rc; + rc = do_test(start_broker, proto_ver=5) + if rc: + return rc; + return 0 + +if __name__ == '__main__': + all_tests(True) diff --git a/test/broker/Makefile b/test/broker/Makefile index b2c86788..a65874c2 100644 --- a/test/broker/Makefile +++ b/test/broker/Makefile @@ -125,6 +125,7 @@ msg_sequence_test: ./06-bridge-reconnect-local-out.py 07 : + ./07-will-control.py ./07-will-delay-invalid-573191.py ./07-will-delay-reconnect.py ./07-will-delay-recover.py diff --git a/test/broker/test.py b/test/broker/test.py index 602b97bc..4afee881 100755 --- a/test/broker/test.py +++ b/test/broker/test.py @@ -101,6 +101,7 @@ tests = [ (3, './06-bridge-per-listener-settings.py'), (2, './06-bridge-reconnect-local-out.py'), + (1, './07-will-control.py'), (1, './07-will-delay-invalid-573191.py'), (1, './07-will-delay-reconnect.py'), (1, './07-will-delay-recover.py'), From 44b94875b597458e69b06878cc468a7c3c94f41b Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Thu, 8 Jun 2023 22:45:31 +0100 Subject: [PATCH 51/79] mosquitto_passwd uses mkstemp() for backup files. --- ChangeLog.txt | 3 +++ apps/mosquitto_passwd/mosquitto_passwd.c | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index e382b41c..2c4e7164 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -29,6 +29,9 @@ Clients: - Fix incorrect topic-alias property value in mosquitto_sub json output. - Fix confusing message on TLS certificate verification. Closes #2746. +Apps: +- mosquitto_passwd uses mkstemp() for backup files. + 2.0.15 - 2022-08-16 =================== diff --git a/apps/mosquitto_passwd/mosquitto_passwd.c b/apps/mosquitto_passwd/mosquitto_passwd.c index 0a5ba722..262f89cf 100644 --- a/apps/mosquitto_passwd/mosquitto_passwd.c +++ b/apps/mosquitto_passwd/mosquitto_passwd.c @@ -370,15 +370,27 @@ static int copy_contents(FILE *src, FILE *dest) return 0; } -static int create_backup(const char *backup_file, FILE *fptr) +static int create_backup(char *backup_file, FILE *fptr) { FILE *fbackup; +#ifdef WIN32 fbackup = mosquitto__fopen(backup_file, "wt", true); +#else + int fd; + umask(077); + fd = mkstemp(backup_file); + if(fd < 0){ + fprintf(stderr, "Error creating backup password file \"%s\", not continuing.\n", backup_file); + return 1; + } + fbackup = fdopen(fd, "wt"); +#endif if(!fbackup){ fprintf(stderr, "Error creating backup password file \"%s\", not continuing.\n", backup_file); return 1; } + if(copy_contents(fptr, fbackup)){ fprintf(stderr, "Error copying data to backup password file \"%s\", not continuing.\n", backup_file); fclose(fbackup); @@ -617,13 +629,13 @@ int main(int argc, char *argv[]) return 1; } - backup_file = malloc((size_t)strlen(password_file)+5); + backup_file = malloc((size_t)strlen(password_file)+strlen(".backup.XXXXXX")); if(!backup_file){ fprintf(stderr, "Error: Out of memory.\n"); free(password_file); return 1; } - snprintf(backup_file, strlen(password_file)+5, "%s.tmp", password_file); + snprintf(backup_file, strlen(password_file)+5, "%s.backup.XXXXXX", password_file); free(password_file); password_file = NULL; From 02d36f99467dfe468055372c2d65c11213a92d14 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Thu, 8 Jun 2023 23:12:18 +0100 Subject: [PATCH 52/79] Broker now validates usernames provided over TLS are valid UTF-8. --- ChangeLog.txt | 4 +++- src/handle_connect.c | 15 +++++++++++++-- src/net.c | 4 ++++ src/plugin_public.c | 3 +++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 2c4e7164..dbe24208 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,5 +1,7 @@ Security: -- Broker will reject Will messages that attempt to publish to $CONTROL/. +- Broker will now reject Will messages that attempt to publish to $CONTROL/. +- Broker now validates usernames provided in a TLS certificate or TLS-PSK + identity are valid UTF-8. Broker: - Fix $SYS messages being expired after 60 seconds and hence unchanged values diff --git a/src/handle_connect.c b/src/handle_connect.c index 63824b0c..58bea7b3 100644 --- a/src/handle_connect.c +++ b/src/handle_connect.c @@ -794,11 +794,22 @@ int handle__connect(struct mosquitto *context) rc = MOSQ_ERR_AUTH; goto handle_connect_error; } + const char *new_username; #if OPENSSL_VERSION_NUMBER < 0x10100000L - context->username = mosquitto__strdup((char *) ASN1_STRING_data(name_asn1)); + new_username = (const char *) ASN1_STRING_data(name_asn1); #else - context->username = mosquitto__strdup((char *) ASN1_STRING_get0_data(name_asn1)); + new_username = (const char *) ASN1_STRING_get0_data(name_asn1); #endif + if(mosquitto_validate_utf8(new_username, (int)strlen(new_username))){ + if(context->protocol == mosq_p_mqtt5){ + send__connack(context, 0, MQTT_RC_BAD_USERNAME_OR_PASSWORD, NULL); + }else{ + send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL); + } + X509_free(client_cert); + return MOSQ_ERR_AUTH; + } + context->username = mosquitto__strdup(new_username); if(!context->username){ if(context->protocol == mosq_p_mqtt5){ send__connack(context, 0, MQTT_RC_SERVER_UNAVAILABLE, NULL); diff --git a/src/net.c b/src/net.c index 51452307..8fab9b3d 100644 --- a/src/net.c +++ b/src/net.c @@ -296,6 +296,10 @@ static unsigned int psk_server_callback(SSL *ssl, const char *identity, unsigned } if(listener->use_identity_as_username){ + if(mosquitto_validate_utf8(identity, (int)strlen(identity))){ + mosquitto__free(psk_key); + return 0; + } context->username = mosquitto__strdup(identity); if(!context->username){ mosquitto__free(psk_key); diff --git a/src/plugin_public.c b/src/plugin_public.c index a800090c..cc1c0ded 100644 --- a/src/plugin_public.c +++ b/src/plugin_public.c @@ -244,6 +244,9 @@ int mosquitto_set_username(struct mosquitto *client, const char *username) if(!client) return MOSQ_ERR_INVAL; if(username){ + if(mosquitto_validate_utf8(username, (int)strlen(username))){ + return MOSQ_ERR_MALFORMED_UTF8; + } u_dup = mosquitto__strdup(username); if(!u_dup) return MOSQ_ERR_NOMEM; }else{ From 67ac8cbe1706f9bf9b0c1d99add8226b923ed32f Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Thu, 8 Jun 2023 23:21:32 +0100 Subject: [PATCH 53/79] mosquitto_memcmp_const is now more constant time. --- ChangeLog.txt | 1 + plugins/dynamic-security/auth.c | 4 +--- src/password_mosq.c | 4 +--- src/security_default.c | 4 +--- 4 files changed, 4 insertions(+), 9 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index dbe24208..579944dc 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -19,6 +19,7 @@ Broker: - Broker will log warnings if sensitive files are world readable/writable, or if the owner/group is not the same as the user/group the broker is running as. In future versions the broker will refuse to open these files. +- mosquitto_memcmp_const is now more constant time. Client library: - Use CLOCK_BOOTTIME when available, to keep track of time. This solves the diff --git a/plugins/dynamic-security/auth.c b/plugins/dynamic-security/auth.c index 1f62e1fb..03e4f6b9 100644 --- a/plugins/dynamic-security/auth.c +++ b/plugins/dynamic-security/auth.c @@ -163,9 +163,7 @@ static int memcmp_const(const void *a, const void *b, size_t len) if(!a || !b) return 1; for(i=0; i Date: Fri, 9 Jun 2023 00:37:42 +0100 Subject: [PATCH 54/79] Only register with DLT if DLT logging is enabled. --- ChangeLog.txt | 1 + src/logging.c | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 579944dc..24f79e5d 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -20,6 +20,7 @@ Broker: if the owner/group is not the same as the user/group the broker is running as. In future versions the broker will refuse to open these files. - mosquitto_memcmp_const is now more constant time. +- Only register with DLT if DLT logging is enabled. Client library: - Use CLOCK_BOOTTIME when available, to keep track of time. This solves the diff --git a/src/logging.c b/src/logging.c index 162a6762..a7e62443 100644 --- a/src/logging.c +++ b/src/logging.c @@ -134,10 +134,12 @@ int log__init(struct mosquitto__config *config) setlinebuf(stdout); } #ifdef WITH_DLT - dlt_fifo_check(); - if(dlt_allowed){ - DLT_REGISTER_APP("MQTT","mosquitto log"); - dlt_register_context(&dltContext, "MQTT", "mosquitto DLT context"); + if(log_destinations & MQTT3_LOG_DLT){ + dlt_fifo_check(); + if(dlt_allowed){ + DLT_REGISTER_APP("MQTT","mosquitto log"); + dlt_register_context(&dltContext, "MQTT", "mosquitto DLT context"); + } } #endif return rc; From 411ed41c27691190c6119ca6d5728e49a03c278a Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Fri, 9 Jun 2023 22:39:57 +0100 Subject: [PATCH 55/79] Dynsec: Simplify config load --- plugins/dynamic-security/plugin.c | 33 +++++-------------------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/plugins/dynamic-security/plugin.c b/plugins/dynamic-security/plugin.c index 7a1119c7..b8943bd5 100644 --- a/plugins/dynamic-security/plugin.c +++ b/plugins/dynamic-security/plugin.c @@ -476,37 +476,14 @@ int mosquitto_plugin_version(int supported_version_count, const int *supported_v static int dynsec__general_config_load(cJSON *tree) { - cJSON *j_default_access, *jtmp; + cJSON *j_default_access; j_default_access = cJSON_GetObjectItem(tree, "defaultACLAccess"); if(j_default_access && cJSON_IsObject(j_default_access)){ - jtmp = cJSON_GetObjectItem(j_default_access, ACL_TYPE_PUB_C_SEND); - if(jtmp && cJSON_IsBool(jtmp)){ - default_access.publish_c_send = cJSON_IsTrue(jtmp); - }else{ - default_access.publish_c_send = false; - } - - jtmp = cJSON_GetObjectItem(j_default_access, ACL_TYPE_PUB_C_RECV); - if(jtmp && cJSON_IsBool(jtmp)){ - default_access.publish_c_recv = cJSON_IsTrue(jtmp); - }else{ - default_access.publish_c_recv = false; - } - - jtmp = cJSON_GetObjectItem(j_default_access, ACL_TYPE_SUB_GENERIC); - if(jtmp && cJSON_IsBool(jtmp)){ - default_access.subscribe = cJSON_IsTrue(jtmp); - }else{ - default_access.subscribe = false; - } - - jtmp = cJSON_GetObjectItem(j_default_access, ACL_TYPE_UNSUB_GENERIC); - if(jtmp && cJSON_IsBool(jtmp)){ - default_access.unsubscribe = cJSON_IsTrue(jtmp); - }else{ - default_access.unsubscribe = false; - } + json_get_bool(j_default_access, ACL_TYPE_PUB_C_SEND, &default_access.publish_c_send, true, false); + json_get_bool(j_default_access, ACL_TYPE_PUB_C_RECV, &default_access.publish_c_recv, true, false); + json_get_bool(j_default_access, ACL_TYPE_SUB_GENERIC, &default_access.subscribe, true, false); + json_get_bool(j_default_access, ACL_TYPE_UNSUB_GENERIC, &default_access.unsubscribe, true, false); } return MOSQ_ERR_SUCCESS; } From a3c680fbb00a0019573fb84c29332e845e6efcad Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Fri, 9 Jun 2023 23:34:10 +0100 Subject: [PATCH 56/79] Fix for Eclipse #581199 --- lib/packet_mosq.c | 2 +- test/broker/01-bad-initial-packets.py | 78 +++++++++++++++++++++++++++ test/broker/Makefile | 1 + test/broker/test.py | 1 + 4 files changed, 81 insertions(+), 1 deletion(-) create mode 100755 test/broker/01-bad-initial-packets.py diff --git a/lib/packet_mosq.c b/lib/packet_mosq.c index da2a6080..d737c185 100644 --- a/lib/packet_mosq.c +++ b/lib/packet_mosq.c @@ -378,7 +378,7 @@ int packet__read(struct mosquitto *mosq) #ifdef WITH_BROKER G_BYTES_RECEIVED_INC(1); /* Clients must send CONNECT as their first command. */ - if(!(mosq->bridge) && state == mosq_cs_connected && (byte&0xF0) != CMD_CONNECT){ + if(!(mosq->bridge) && state == mosq_cs_new && (byte&0xF0) != CMD_CONNECT){ return MOSQ_ERR_PROTOCOL; } #endif diff --git a/test/broker/01-bad-initial-packets.py b/test/broker/01-bad-initial-packets.py new file mode 100755 index 00000000..52f2de95 --- /dev/null +++ b/test/broker/01-bad-initial-packets.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 + +# Test whether non-CONNECT packets as an initial packet can cause excess memory use + +from mosq_test_helper import * +import psutil + +def write_config(filename, port): + with open(filename, 'w') as f: + f.write(f"listener {port}\n") + f.write("allow_anonymous true\n") + f.write("sys_interval 1\n") + +def do_send(port, socks, payload): + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + socks.append(sock) + sock.connect(("127.0.0.1", port)) + try: + sock.send(payload) + except ConnectionResetError: + pass + +def do_test(port): + rc = 1 + + conf_file = os.path.basename(__file__).replace('.py', '.conf') + write_config(conf_file, port) + broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True) + + try: + socks = [] + + do_send(port, socks, b"\x20\x80\x80\x80t" + b"\01"*100000000) # CONNACK + do_send(port, socks, b"\x30\x80\x80\x80t" + b"\01"*100000000) # PUBLISH + do_send(port, socks, b"\x40\x80\x80\x80t" + b"\01"*100000000) # PUBACK + do_send(port, socks, b"\x50\x80\x80\x80t" + b"\01"*100000000) # PUBREC + do_send(port, socks, b"\x60\x80\x80\x80t" + b"\01"*100000000) # PUBREL + do_send(port, socks, b"\x70\x80\x80\x80t" + b"\01"*100000000) # PUBCOMP + do_send(port, socks, b"\x80\x80\x80\x80t" + b"\01"*100000000) # SUBSCRIBE + do_send(port, socks, b"\x90\x80\x80\x80t" + b"\01"*100000000) # SUBACK + do_send(port, socks, b"\xA0\x80\x80\x80t" + b"\01"*100000000) # UNSUBSCRIBE + do_send(port, socks, b"\xB0\x80\x80\x80t" + b"\01"*100000000) # UNSUBACK + do_send(port, socks, b"\xC0\x80\x80\x80t" + b"\01"*100000000) # PINGREQ + do_send(port, socks, b"\xD0\x80\x80\x80t" + b"\01"*100000000) # PINGRESP + do_send(port, socks, b"\xE0\x80\x80\x80t" + b"\01"*100000000) # DISCONNECT + do_send(port, socks, b"\xF0\x80\x80\x80t" + b"\01"*100000000) # AUTH + + mem = psutil.Process(broker.pid).memory_info().vms + + for s in socks: + s.close() + + limit = 25000000 + if mem > limit: + raise mosq_test.TestError(f"Process memory {mem} greater than limit of {limit}") + + rc = 0 + except MemoryError: + print("Memory error!") + except Exception as e: + print(e) + except mosq_test.TestError: + pass + finally: + os.remove(conf_file) + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde.decode('utf-8')) + exit(rc) + + +port = mosq_test.get_port() + +do_test(port) + +exit(0) diff --git a/test/broker/Makefile b/test/broker/Makefile index a65874c2..94cc8026 100644 --- a/test/broker/Makefile +++ b/test/broker/Makefile @@ -23,6 +23,7 @@ msg_sequence_test: ./msg_sequence_test.py 01 : + ./01-bad-initial-packets.py ./01-connect-575314.py ./01-connect-allow-anonymous.py ./01-connect-disconnect-v5.py diff --git a/test/broker/test.py b/test/broker/test.py index 4afee881..649746d3 100755 --- a/test/broker/test.py +++ b/test/broker/test.py @@ -5,6 +5,7 @@ import ptest tests = [ #(ports required, 'path'), + (1, './01-bad-initial-packets.py'), (1, './01-connect-575314.py'), (1, './01-connect-allow-anonymous.py'), (1, './01-connect-disconnect-v5.py'), From b1c29e881e74b5906f20a7584f23d5e01358daea Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Fri, 9 Jun 2023 23:56:55 +0100 Subject: [PATCH 57/79] Fix potential file persistence crash. Identified by OS-TIF audit. TOB-MOSQ-CR-5 --- ChangeLog.txt | 1 + src/persist_write.c | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 24f79e5d..231e4c3a 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -2,6 +2,7 @@ Security: - Broker will now reject Will messages that attempt to publish to $CONTROL/. - Broker now validates usernames provided in a TLS certificate or TLS-PSK identity are valid UTF-8. +- Fix potential crash when loading invalid persitence file. Broker: - Fix $SYS messages being expired after 60 seconds and hence unchanged values diff --git a/src/persist_write.c b/src/persist_write.c index f0949f8a..954e36a4 100644 --- a/src/persist_write.c +++ b/src/persist_write.c @@ -47,8 +47,6 @@ static int persist__client_messages_save(FILE *db_fptr, struct mosquitto *contex assert(db_fptr); assert(context); - memset(&chunk, 0, sizeof(struct P_client_msg)); - cmsg = queue; while(cmsg){ if(!strncmp(cmsg->store->topic, "$SYS", 4) @@ -61,6 +59,8 @@ static int persist__client_messages_save(FILE *db_fptr, struct mosquitto *contex continue; } + memset(&chunk, 0, sizeof(struct P_client_msg)); + chunk.F.store_id = cmsg->store->db_id; chunk.F.mid = cmsg->mid; chunk.F.id_len = (uint16_t)strlen(context->id); @@ -91,8 +91,6 @@ static int persist__message_store_save(FILE *db_fptr) assert(db_fptr); - memset(&chunk, 0, sizeof(struct P_msg_store)); - stored = db.msg_store; while(stored){ if(stored->ref_count < 1 || stored->topic == NULL){ @@ -100,6 +98,8 @@ static int persist__message_store_save(FILE *db_fptr) continue; } + memset(&chunk, 0, sizeof(struct P_msg_store)); + if(!strncmp(stored->topic, "$SYS", 4)){ if(stored->ref_count <= 1 && stored->dest_id_count == 0){ /* $SYS messages that are only retained shouldn't be persisted. */ @@ -164,9 +164,9 @@ static int persist__client_save(FILE *db_fptr) assert(db_fptr); - memset(&chunk, 0, sizeof(struct P_client)); - HASH_ITER(hh_id, db.contexts_by_id, context, ctxt_tmp){ + memset(&chunk, 0, sizeof(struct P_client)); + if(context && #ifdef WITH_BRIDGE ((!context->bridge && context->clean_start == false) @@ -224,8 +224,6 @@ static int persist__subs_save(FILE *db_fptr, struct mosquitto__subhier *node, co size_t slen; int rc; - memset(&sub_chunk, 0, sizeof(struct P_sub)); - slen = strlen(topic) + node->topic_len + 2; thistopic = mosquitto__malloc(sizeof(char)*slen); if(!thistopic) return MOSQ_ERR_NOMEM; @@ -238,6 +236,8 @@ static int persist__subs_save(FILE *db_fptr, struct mosquitto__subhier *node, co sub = node->subs; while(sub){ if(sub->context->clean_start == false && sub->context->id){ + memset(&sub_chunk, 0, sizeof(struct P_sub)); + sub_chunk.F.identifier = sub->identifier; sub_chunk.F.id_len = (uint16_t)strlen(sub->context->id); sub_chunk.F.topic_len = (uint16_t)strlen(thistopic); @@ -281,10 +281,10 @@ static int persist__retain_save(FILE *db_fptr, struct mosquitto__retainhier *nod struct P_retain retain_chunk; int rc; - memset(&retain_chunk, 0, sizeof(struct P_retain)); - if(node->retained && strncmp(node->retained->topic, "$SYS", 4)){ /* Don't save $SYS messages. */ + memset(&retain_chunk, 0, sizeof(struct P_retain)); + retain_chunk.F.store_id = node->retained->db_id; rc = persist__chunk_retain_write_v6(db_fptr, &retain_chunk); if(rc){ From 6113eac95a9df634fbc858be542c4a0456bfe7b9 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 13 Jun 2023 11:22:03 +0100 Subject: [PATCH 58/79] Fix for CVE-2023-28366 --- ChangeLog.txt | 3 ++ lib/packet_mosq.c | 15 ++++++++ src/context.c | 41 ++++++++++++--------- src/database.c | 25 +++++++------ src/handle_publish.c | 35 ++++++++++++------ src/mosquitto_broker_internal.h | 4 +-- test/broker/03-publish-qos2-dup.py | 58 ++++++++++++++++++++++++++++++ test/broker/Makefile | 1 + test/broker/test.py | 1 + www/pages/security.md | 3 ++ 10 files changed, 147 insertions(+), 39 deletions(-) create mode 100755 test/broker/03-publish-qos2-dup.py diff --git a/ChangeLog.txt b/ChangeLog.txt index 231e4c3a..412d3b4d 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,4 +1,7 @@ Security: +- CVE-2023-28366: Fix memory leak in broker when clients send multiple QoS 2 + messages with the same message ID, but then never respond to the PUBREC + commands. - Broker will now reject Will messages that attempt to publish to $CONTROL/. - Broker now validates usernames provided in a TLS certificate or TLS-PSK identity are valid UTF-8. diff --git a/lib/packet_mosq.c b/lib/packet_mosq.c index d737c185..8c06b942 100644 --- a/lib/packet_mosq.c +++ b/lib/packet_mosq.c @@ -152,6 +152,21 @@ int packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet) packet->next = NULL; pthread_mutex_lock(&mosq->out_packet_mutex); + +#ifdef WITH_BROKER + if(mosq->out_packet_count >= db.config->max_queued_messages){ + mosquitto__free(packet); + if(mosq->is_dropping == false){ + mosq->is_dropping = true; + log__printf(NULL, MOSQ_LOG_NOTICE, + "Outgoing messages are being dropped for client %s.", + mosq->id); + } + G_MSGS_DROPPED_INC(); + return MOSQ_ERR_SUCCESS; + } +#endif + if(mosq->out_packet){ mosq->out_packet_last->next = packet; }else{ diff --git a/src/context.c b/src/context.c index 923d06f8..3ed49765 100644 --- a/src/context.c +++ b/src/context.c @@ -83,9 +83,9 @@ struct mosquitto *context__init(mosq_sock_t sock) } } context->bridge = NULL; - context->msgs_in.inflight_maximum = db.config->max_inflight_messages; + context->msgs_in.inflight_maximum = 1; context->msgs_out.inflight_maximum = db.config->max_inflight_messages; - context->msgs_in.inflight_quota = db.config->max_inflight_messages; + context->msgs_in.inflight_quota = 1; context->msgs_out.inflight_quota = db.config->max_inflight_messages; context->max_qos = 2; #ifdef WITH_TLS @@ -98,6 +98,27 @@ struct mosquitto *context__init(mosq_sock_t sock) return context; } +static void context__cleanup_out_packets(struct mosquitto *context) +{ + struct mosquitto__packet *packet; + + if(!context) return; + + if(context->current_out_packet){ + packet__cleanup(context->current_out_packet); + mosquitto__free(context->current_out_packet); + context->current_out_packet = NULL; + } + while(context->out_packet){ + packet__cleanup(context->out_packet); + packet = context->out_packet; + context->out_packet = context->out_packet->next; + mosquitto__free(packet); + } + context->out_packet_count = 0; +} + + /* * This will result in any outgoing packets going unsent. If we're disconnected * forcefully then it is usually an error condition and shouldn't be a problem, @@ -106,8 +127,6 @@ struct mosquitto *context__init(mosq_sock_t sock) */ void context__cleanup(struct mosquitto *context, bool force_free) { - struct mosquitto__packet *packet; - if(!context) return; if(force_free){ @@ -121,6 +140,7 @@ void context__cleanup(struct mosquitto *context, bool force_free) #endif alias__free_all(context); + context__cleanup_out_packets(context); mosquitto__free(context->auth_method); context->auth_method = NULL; @@ -148,18 +168,7 @@ void context__cleanup(struct mosquitto *context, bool force_free) context->id = NULL; } packet__cleanup(&(context->in_packet)); - if(context->current_out_packet){ - packet__cleanup(context->current_out_packet); - mosquitto__free(context->current_out_packet); - context->current_out_packet = NULL; - } - while(context->out_packet){ - packet__cleanup(context->out_packet); - packet = context->out_packet; - context->out_packet = context->out_packet->next; - mosquitto__free(packet); - } - context->out_packet_count = 0; + context__cleanup_out_packets(context); #if defined(WITH_BROKER) && defined(__GLIBC__) && defined(WITH_ADNS) if(context->adns){ gai_cancel(context->adns); diff --git a/src/database.c b/src/database.c index 5e77a281..1a926f50 100644 --- a/src/database.c +++ b/src/database.c @@ -555,7 +555,7 @@ int db__message_insert(struct mosquitto *context, uint16_t mid, enum mosquitto_m } #endif - msg = mosquitto__malloc(sizeof(struct mosquitto_client_msg)); + msg = mosquitto__calloc(1, sizeof(struct mosquitto_client_msg)); if(!msg) return MOSQ_ERR_NOMEM; msg->prev = NULL; msg->next = NULL; @@ -613,6 +613,8 @@ int db__message_insert(struct mosquitto *context, uint16_t mid, enum mosquitto_m if(dir == mosq_md_out && msg->qos > 0 && state != mosq_ms_queued){ util__decrement_send_quota(context); + }else if(dir == mosq_md_in && msg->qos > 0 && state != mosq_ms_queued){ + util__decrement_receive_quota(context); } if(dir == mosq_md_out && update){ @@ -796,23 +798,24 @@ int db__message_store(const struct mosquitto *source, struct mosquitto_msg_store return MOSQ_ERR_SUCCESS; } -int db__message_store_find(struct mosquitto *context, uint16_t mid, struct mosquitto_msg_store **stored) +int db__message_store_find(struct mosquitto *context, uint16_t mid, struct mosquitto_client_msg **client_msg) { - struct mosquitto_client_msg *tail; + struct mosquitto_client_msg *cmsg; + + *client_msg = NULL; if(!context) return MOSQ_ERR_INVAL; - *stored = NULL; - DL_FOREACH(context->msgs_in.inflight, tail){ - if(tail->store->source_mid == mid){ - *stored = tail->store; + DL_FOREACH(context->msgs_in.inflight, cmsg){ + if(cmsg->store->source_mid == mid){ + *client_msg = cmsg; return MOSQ_ERR_SUCCESS; } } - DL_FOREACH(context->msgs_in.queued, tail){ - if(tail->store->source_mid == mid){ - *stored = tail->store; + DL_FOREACH(context->msgs_in.queued, cmsg){ + if(cmsg->store->source_mid == mid){ + *client_msg = cmsg; return MOSQ_ERR_SUCCESS; } } @@ -914,6 +917,7 @@ static int db__message_reconnect_reset_incoming(struct mosquitto *context) }else{ /* Message state can be preserved here because it should match * whatever the client has got. */ + msg->dup = 0; } } @@ -924,6 +928,7 @@ static int db__message_reconnect_reset_incoming(struct mosquitto *context) * will be sent out of order. */ DL_FOREACH_SAFE(context->msgs_in.queued, msg, tmp){ + msg->dup = 0; db__msg_add_to_queued_stats(&context->msgs_in, msg); if(db__ready_for_flight(context, mosq_md_in, msg->qos)){ switch(msg->qos){ diff --git a/src/handle_publish.c b/src/handle_publish.c index 111f0316..d3aa5ce2 100644 --- a/src/handle_publish.c +++ b/src/handle_publish.c @@ -42,6 +42,7 @@ int handle__publish(struct mosquitto *context) uint8_t header = context->in_packet.command; int res = 0; struct mosquitto_msg_store *msg, *stored = NULL; + struct mosquitto_client_msg *cmsg_stored = NULL; size_t len; uint16_t slen; char *topic_mount; @@ -287,24 +288,24 @@ int handle__publish(struct mosquitto *context) } if(msg->qos > 0){ - db__message_store_find(context, msg->source_mid, &stored); + db__message_store_find(context, msg->source_mid, &cmsg_stored); } - if(stored && msg->source_mid != 0 && - (stored->qos != msg->qos - || stored->payloadlen != msg->payloadlen - || strcmp(stored->topic, msg->topic) - || memcmp(stored->payload, msg->payload, msg->payloadlen) )){ + if(cmsg_stored && cmsg_stored->store && msg->source_mid != 0 && + (cmsg_stored->store->qos != msg->qos + || cmsg_stored->store->payloadlen != msg->payloadlen + || strcmp(cmsg_stored->store->topic, msg->topic) + || memcmp(cmsg_stored->store->payload, msg->payload, msg->payloadlen) )){ log__printf(NULL, MOSQ_LOG_WARNING, "Reused message ID %u from %s detected. Clearing from storage.", msg->source_mid, context->id); db__message_remove_incoming(context, msg->source_mid); - stored = NULL; + cmsg_stored = NULL; } - if(!stored){ + if(!cmsg_stored){ if(msg->qos == 0 || db__ready_for_flight(context, mosq_md_in, msg->qos) - || db__ready_for_queue(context, msg->qos, &context->msgs_in)){ + ){ dup = 0; rc = db__message_store(context, msg, message_expiry_interval, 0, mosq_mo_client); @@ -316,10 +317,13 @@ int handle__publish(struct mosquitto *context) } stored = msg; msg = NULL; + dup = 0; }else{ db__msg_store_free(msg); msg = NULL; - dup = 1; + stored = cmsg_stored->store; + cmsg_stored->dup++; + dup = cmsg_stored->dup; } switch(stored->qos){ @@ -345,11 +349,17 @@ int handle__publish(struct mosquitto *context) }else{ res = 0; } + /* db__message_insert() returns 2 to indicate dropped message * due to queue. This isn't an error so don't disconnect them. */ /* FIXME - this is no longer necessary due to failing early above */ if(!res){ - if(send__pubrec(context, stored->source_mid, 0, NULL)) rc = 1; + if(dup == 0 || dup == 1){ + rc2 = send__pubrec(context, stored->source_mid, 0, NULL); + if(rc2) rc = rc2; + }else{ + return MOSQ_ERR_PROTOCOL; + } }else if(res == 1){ rc = 1; } @@ -374,6 +384,9 @@ process_bad_message: } db__msg_store_free(msg); } + if(context->out_packet_count >= db.config->max_queued_messages){ + rc = MQTT_RC_QUOTA_EXCEEDED; + } return rc; } diff --git a/src/mosquitto_broker_internal.h b/src/mosquitto_broker_internal.h index c28eaa2a..2cfa9d2b 100644 --- a/src/mosquitto_broker_internal.h +++ b/src/mosquitto_broker_internal.h @@ -394,7 +394,7 @@ struct mosquitto_client_msg{ bool retain; enum mosquitto_msg_direction direction; enum mosquitto_msg_state state; - bool dup; + uint8_t dup; }; @@ -651,7 +651,7 @@ void db__message_dequeue_first(struct mosquitto *context, struct mosquitto_msg_d int db__messages_delete(struct mosquitto *context, bool force_free); int db__messages_easy_queue(struct mosquitto *context, const char *topic, uint8_t qos, uint32_t payloadlen, const void *payload, int retain, uint32_t message_expiry_interval, mosquitto_property **properties); int db__message_store(const struct mosquitto *source, struct mosquitto_msg_store *stored, uint32_t message_expiry_interval, dbid_t store_id, enum mosquitto_msg_origin origin); -int db__message_store_find(struct mosquitto *context, uint16_t mid, struct mosquitto_msg_store **stored); +int db__message_store_find(struct mosquitto *context, uint16_t mid, struct mosquitto_client_msg **client_msg); void db__msg_store_add(struct mosquitto_msg_store *store); void db__msg_store_remove(struct mosquitto_msg_store *store); void db__msg_store_ref_inc(struct mosquitto_msg_store *store); diff --git a/test/broker/03-publish-qos2-dup.py b/test/broker/03-publish-qos2-dup.py new file mode 100755 index 00000000..70834fab --- /dev/null +++ b/test/broker/03-publish-qos2-dup.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 + +from mosq_test_helper import * + +def do_test(proto_ver): + rc = 1 + connect_packet = mosq_test.gen_connect("03-pub-qos2-dup-test", proto_ver=proto_ver) + connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) + + mid = 1 + publish_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="message", proto_ver=proto_ver, dup=1) + pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver) + + disconnect_packet = mosq_test.gen_disconnect(reason_code=130, proto_ver=proto_ver) + + port = mosq_test.get_port() + broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) + + try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) + mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, "pubrec 1") + mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, "pubrec 2") + if proto_ver == 5: + mosq_test.do_send_receive(sock, publish_packet, disconnect_packet, "disconnect") + rc = 0 + else: + try: + mosq_test.do_send_receive(sock, publish_packet, b"", "disconnect1") + rc = 0 + except BrokenPipeError: + rc = 0 + + sock.close() + except Exception as e: + print(e) + except mosq_test.TestError: + pass + finally: + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde.decode('utf-8')) + print("proto_ver=%d" % (proto_ver)) + exit(rc) + + +def all_tests(): + rc = do_test(proto_ver=4) + if rc: + return rc; + rc = do_test(proto_ver=5) + if rc: + return rc; + return 0 + +if __name__ == '__main__': + all_tests() diff --git a/test/broker/Makefile b/test/broker/Makefile index 94cc8026..e66c7ffc 100644 --- a/test/broker/Makefile +++ b/test/broker/Makefile @@ -84,6 +84,7 @@ msg_sequence_test: ./03-publish-qos1-no-subscribers-v5.py ./03-publish-qos1-retain-disabled.py ./03-publish-qos1.py + ./03-publish-qos2-dup.py ./03-publish-qos2-max-inflight.py ./03-publish-qos2.py diff --git a/test/broker/test.py b/test/broker/test.py index 649746d3..e8956408 100755 --- a/test/broker/test.py +++ b/test/broker/test.py @@ -64,6 +64,7 @@ tests = [ (1, './03-publish-qos1-no-subscribers-v5.py'), (1, './03-publish-qos1-retain-disabled.py'), (1, './03-publish-qos1.py'), + (1, './03-publish-qos2-dup.py'), (1, './03-publish-qos2-max-inflight.py'), (1, './03-publish-qos2.py'), diff --git a/www/pages/security.md b/www/pages/security.md index 2f124cca..ddc4423f 100644 --- a/www/pages/security.md +++ b/www/pages/security.md @@ -19,6 +19,9 @@ follow the steps on [Eclipse Security] page to report it. Listed with most recent first. Further information on security related issues can be found in the [security category]. +* June 2023: [CVE-2023-28366]: Clients sending unacknowledged QoS 2 messages + with duplicate message ids cause a memory leak. Affecting versions **1.3.2** + to **2.0.15** inclusive, fixed in **2.0.16**. * August 2022: Deleting the anonymous group in the dynamic security plugin could lead to a crash. Affecting versions **2.0.0** to **2.0.14** inclusive, fixed in **2.0.15**. From 41db41761d2970806c5156e8af75f4d0b49dbcda Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 13 Jun 2023 11:47:32 +0100 Subject: [PATCH 59/79] Fix test running under valgrind --- test/broker/01-bad-initial-packets.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/broker/01-bad-initial-packets.py b/test/broker/01-bad-initial-packets.py index 52f2de95..7e2ca777 100755 --- a/test/broker/01-bad-initial-packets.py +++ b/test/broker/01-bad-initial-packets.py @@ -50,7 +50,10 @@ def do_test(port): for s in socks: s.close() - limit = 25000000 + if os.environ.get('MOSQ_USE_VALGRIND') is None: + limit = 25000000 + else: + limit = 120000000 if mem > limit: raise mosq_test.TestError(f"Process memory {mem} greater than limit of {limit}") From fa9979ce687139b0fed40bbf0804c49c803c7ef7 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 13 Jun 2023 16:44:20 +0100 Subject: [PATCH 60/79] Update mosquitto-tls to suggest modern cypher. --- man/mosquitto-tls.7.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/man/mosquitto-tls.7.xml b/man/mosquitto-tls.7.xml index 6a344103..e10a0ed4 100644 --- a/man/mosquitto-tls.7.xml +++ b/man/mosquitto-tls.7.xml @@ -47,7 +47,7 @@ Server Generate a server key. - openssl genrsa -des3 -out server.key 2048 + openssl genrsa -aes256 -out server.key 2048 Generate a server key without encryption. @@ -71,7 +71,7 @@ Client Generate a client key. - openssl genrsa -des3 -out client.key 2048 + openssl genrsa -aes256 -out client.key 2048 Generate a certificate signing request to send to the CA. From 8bc047511a9d1e6bcf29c40cdc9d7abcc0b49fc2 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 13 Jun 2023 13:50:09 +0100 Subject: [PATCH 61/79] Fix any possible case where a json string might be incorrectly loaded. --- ChangeLog.txt | 3 + plugins/dynamic-security/clients.c | 96 ++++++++++++++-------------- plugins/dynamic-security/groups.c | 70 +++++++++++---------- plugins/dynamic-security/plugin.c | 22 +++---- plugins/dynamic-security/rolelist.c | 9 +-- plugins/dynamic-security/roles.c | 98 ++++++++++++++--------------- 6 files changed, 150 insertions(+), 148 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 412d3b4d..816ba8eb 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -25,6 +25,9 @@ Broker: as. In future versions the broker will refuse to open these files. - mosquitto_memcmp_const is now more constant time. - Only register with DLT if DLT logging is enabled. +- Fix any possible case where a json string might be incorrectly loaded. This + could have caused a crash if a textname or textdescription field of a role was + not a string, when loading the dynsec config from file only. Client library: - Use CLOCK_BOOTTIME when available, to keep track of time. This solves the diff --git a/plugins/dynamic-security/clients.c b/plugins/dynamic-security/clients.c index 78d53729..07ae7a42 100644 --- a/plugins/dynamic-security/clients.c +++ b/plugins/dynamic-security/clients.c @@ -106,14 +106,12 @@ void dynsec_clients__cleanup(void) int dynsec_clients__config_load(cJSON *tree) { - cJSON *j_clients, *j_client, *jtmp, *j_roles, *j_role; - cJSON *j_salt, *j_password, *j_iterations; + cJSON *j_clients, *j_client, *j_roles, *j_role; struct dynsec__client *client; struct dynsec__role *role; unsigned char *buf; int buf_len; int priority; - int iterations; j_clients = cJSON_GetObjectItem(tree, "clients"); if(j_clients == NULL){ @@ -132,41 +130,34 @@ int dynsec_clients__config_load(cJSON *tree) } /* Username */ - jtmp = cJSON_GetObjectItem(j_client, "username"); - if(jtmp == NULL || !cJSON_IsString(jtmp)){ + char *username; + json_get_string(j_client, "username", &username, false); + if(!username){ mosquitto_free(client); continue; } - client->username = mosquitto_strdup(jtmp->valuestring); + client->username = mosquitto_strdup(username); if(client->username == NULL){ mosquitto_free(client); continue; } - jtmp = cJSON_GetObjectItem(j_client, "disabled"); - if(jtmp && cJSON_IsBool(jtmp)){ - client->disabled = cJSON_IsTrue(jtmp); + bool disabled; + if(json_get_bool(j_client, "disabled", &disabled, false, false) == MOSQ_ERR_SUCCESS){ + client->disabled = disabled; } /* Salt */ - j_salt = cJSON_GetObjectItem(j_client, "salt"); - j_password = cJSON_GetObjectItem(j_client, "password"); - j_iterations = cJSON_GetObjectItem(j_client, "iterations"); + char *salt, *password; + int iterations; + json_get_string(j_client, "salt", &salt, false); + json_get_string(j_client, "password", &password, false); + json_get_int(j_client, "iterations", &iterations, false, -1); - if(j_salt && cJSON_IsString(j_salt) - && j_password && cJSON_IsString(j_password) - && j_iterations && cJSON_IsNumber(j_iterations)){ + if(salt && password && iterations > 0){ + client->pw.iterations = iterations; - iterations = (int)j_iterations->valuedouble; - if(iterations < 1){ - mosquitto_free(client->username); - mosquitto_free(client); - continue; - }else{ - client->pw.iterations = iterations; - } - - if(dynsec_auth__base64_decode(j_salt->valuestring, &buf, &buf_len) != MOSQ_ERR_SUCCESS + if(dynsec_auth__base64_decode(salt, &buf, &buf_len) != MOSQ_ERR_SUCCESS || buf_len != sizeof(client->pw.salt)){ mosquitto_free(client->username); @@ -176,7 +167,7 @@ int dynsec_clients__config_load(cJSON *tree) memcpy(client->pw.salt, buf, (size_t)buf_len); mosquitto_free(buf); - if(dynsec_auth__base64_decode(j_password->valuestring, &buf, &buf_len) != MOSQ_ERR_SUCCESS + if(dynsec_auth__base64_decode(password, &buf, &buf_len) != MOSQ_ERR_SUCCESS || buf_len != sizeof(client->pw.password_hash)){ mosquitto_free(client->username); @@ -191,9 +182,10 @@ int dynsec_clients__config_load(cJSON *tree) } /* Client id */ - jtmp = cJSON_GetObjectItem(j_client, "clientid"); - if(jtmp != NULL && cJSON_IsString(jtmp)){ - client->clientid = mosquitto_strdup(jtmp->valuestring); + char *clientid; + json_get_string(j_client, "clientid", &clientid, false); + if(clientid){ + client->clientid = mosquitto_strdup(clientid); if(client->clientid == NULL){ mosquitto_free(client->username); mosquitto_free(client); @@ -202,9 +194,10 @@ int dynsec_clients__config_load(cJSON *tree) } /* Text name */ - jtmp = cJSON_GetObjectItem(j_client, "textname"); - if(jtmp != NULL && cJSON_IsString(jtmp)){ - client->text_name = mosquitto_strdup(jtmp->valuestring); + char *textname; + json_get_string(j_client, "textname", &textname, false); + if(textname){ + client->text_name = mosquitto_strdup(textname); if(client->text_name == NULL){ mosquitto_free(client->clientid); mosquitto_free(client->username); @@ -214,9 +207,10 @@ int dynsec_clients__config_load(cJSON *tree) } /* Text description */ - jtmp = cJSON_GetObjectItem(j_client, "textdescription"); - if(jtmp != NULL && cJSON_IsString(jtmp)){ - client->text_description = mosquitto_strdup(jtmp->valuestring); + char *textdescription; + json_get_string(j_client, "textdescription", &textdescription, false); + if(textdescription){ + client->text_description = mosquitto_strdup(textdescription); if(client->text_description == NULL){ mosquitto_free(client->text_name); mosquitto_free(client->clientid); @@ -231,10 +225,11 @@ int dynsec_clients__config_load(cJSON *tree) if(j_roles && cJSON_IsArray(j_roles)){ cJSON_ArrayForEach(j_role, j_roles){ if(cJSON_IsObject(j_role)){ - jtmp = cJSON_GetObjectItem(j_role, "rolename"); - if(jtmp && cJSON_IsString(jtmp)){ + char *rolename; + json_get_string(j_role, "rolename", &rolename, false); + if(rolename){ json_get_int(j_role, "priority", &priority, true, -1); - role = dynsec_roles__find(jtmp->valuestring); + role = dynsec_roles__find(rolename); dynsec_rolelist__client_add(client, role, priority); } } @@ -326,7 +321,7 @@ int dynsec_clients__process_create(cJSON *j_responses, struct mosquitto *context char *text_name, *text_description; struct dynsec__client *client; int rc; - cJSON *j_groups, *j_group, *jtmp; + cJSON *j_groups, *j_group; int priority; const char *admin_clientid, *admin_username; @@ -438,10 +433,11 @@ int dynsec_clients__process_create(cJSON *j_responses, struct mosquitto *context if(j_groups && cJSON_IsArray(j_groups)){ cJSON_ArrayForEach(j_group, j_groups){ if(cJSON_IsObject(j_group)){ - jtmp = cJSON_GetObjectItem(j_group, "groupname"); - if(jtmp && cJSON_IsString(jtmp)){ + char *groupname; + json_get_string(j_group, "groupname", &groupname, false); + if(groupname){ json_get_int(j_group, "priority", &priority, true, -1); - rc = dynsec_groups__add_client(username, jtmp->valuestring, priority, false); + rc = dynsec_groups__add_client(username, groupname, priority, false); if(rc == ERR_GROUP_NOT_FOUND){ dynsec__command_reply(j_responses, context, "createClient", "Group not found", correlation_data); client__free_item(client); @@ -730,7 +726,7 @@ int dynsec_clients__process_modify(cJSON *j_responses, struct mosquitto *context char *str; int rc; int priority; - cJSON *j_group, *j_groups, *jtmp; + cJSON *j_group, *j_groups; const char *admin_clientid, *admin_username; if(json_get_string(command, "username", &username, false) != MOSQ_ERR_SUCCESS){ @@ -812,9 +808,10 @@ int dynsec_clients__process_modify(cJSON *j_responses, struct mosquitto *context /* Iterate through list to check all groups are valid */ cJSON_ArrayForEach(j_group, j_groups){ if(cJSON_IsObject(j_group)){ - jtmp = cJSON_GetObjectItem(j_group, "groupname"); - if(jtmp && cJSON_IsString(jtmp)){ - group = dynsec_groups__find(jtmp->valuestring); + char *groupname; + json_get_string(j_group, "groupname", &groupname, false); + if(groupname){ + group = dynsec_groups__find(groupname); if(group == NULL){ dynsec__command_reply(j_responses, context, "modifyClient", "'groups' contains an object with a 'groupname' that does not exist", correlation_data); rc = MOSQ_ERR_INVAL; @@ -831,10 +828,11 @@ int dynsec_clients__process_modify(cJSON *j_responses, struct mosquitto *context dynsec__remove_client_from_all_groups(username); cJSON_ArrayForEach(j_group, j_groups){ if(cJSON_IsObject(j_group)){ - jtmp = cJSON_GetObjectItem(j_group, "groupname"); - if(jtmp && cJSON_IsString(jtmp)){ + char *groupname; + json_get_string(j_group, "groupname", &groupname, false); + if(groupname){ json_get_int(j_group, "priority", &priority, true, -1); - dynsec_groups__add_client(username, jtmp->valuestring, priority, false); + dynsec_groups__add_client(username, groupname, priority, false); } } } diff --git a/plugins/dynamic-security/groups.c b/plugins/dynamic-security/groups.c index f26a2ba5..601b06d1 100644 --- a/plugins/dynamic-security/groups.c +++ b/plugins/dynamic-security/groups.c @@ -195,12 +195,12 @@ void dynsec_groups__cleanup(void) int dynsec_groups__config_load(cJSON *tree) { cJSON *j_groups, *j_group; - cJSON *j_clientlist, *j_client, *j_username; - cJSON *j_roles, *j_role, *j_rolename; + cJSON *j_clientlist, *j_client; + cJSON *j_roles, *j_role; struct dynsec__group *group; struct dynsec__role *role; - char *str; + char *groupname; int priority; j_groups = cJSON_GetObjectItem(tree, "groups"); @@ -220,20 +220,21 @@ int dynsec_groups__config_load(cJSON *tree) } /* Group name */ - if(json_get_string(j_group, "groupname", &str, false) != MOSQ_ERR_SUCCESS){ + if(json_get_string(j_group, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){ mosquitto_free(group); continue; } - group->groupname = strdup(str); + group->groupname = strdup(groupname); if(group->groupname == NULL){ mosquitto_free(group); continue; } /* Text name */ - if(json_get_string(j_group, "textname", &str, false) == MOSQ_ERR_SUCCESS){ - if(str){ - group->text_name = strdup(str); + char *textname; + if(json_get_string(j_group, "textname", &textname, false) == MOSQ_ERR_SUCCESS){ + if(textname){ + group->text_name = strdup(textname); if(group->text_name == NULL){ mosquitto_free(group->groupname); mosquitto_free(group); @@ -243,9 +244,10 @@ int dynsec_groups__config_load(cJSON *tree) } /* Text description */ - if(json_get_string(j_group, "textdescription", &str, false) == MOSQ_ERR_SUCCESS){ - if(str){ - group->text_description = strdup(str); + char *textdescription; + if(json_get_string(j_group, "textdescription", &textdescription, false) == MOSQ_ERR_SUCCESS){ + if(textdescription){ + group->text_description = strdup(textdescription); if(group->text_description == NULL){ mosquitto_free(group->text_name); mosquitto_free(group->groupname); @@ -260,10 +262,11 @@ int dynsec_groups__config_load(cJSON *tree) if(j_roles && cJSON_IsArray(j_roles)){ cJSON_ArrayForEach(j_role, j_roles){ if(cJSON_IsObject(j_role)){ - j_rolename = cJSON_GetObjectItem(j_role, "rolename"); - if(j_rolename && cJSON_IsString(j_rolename)){ + char *rolename; + json_get_string(j_role, "rolename", &rolename, false); + if(rolename){ json_get_int(j_role, "priority", &priority, true, -1); - role = dynsec_roles__find(j_rolename->valuestring); + role = dynsec_roles__find(rolename); dynsec_rolelist__group_add(group, role, priority); } } @@ -278,10 +281,11 @@ int dynsec_groups__config_load(cJSON *tree) if(j_clientlist && cJSON_IsArray(j_clientlist)){ cJSON_ArrayForEach(j_client, j_clientlist){ if(cJSON_IsObject(j_client)){ - j_username = cJSON_GetObjectItem(j_client, "username"); - if(j_username && cJSON_IsString(j_username)){ + char *username; + json_get_string(j_client, "username", &username, false); + if(username){ json_get_int(j_client, "priority", &priority, true, -1); - dynsec_groups__add_client(j_username->valuestring, group->groupname, priority, false); + dynsec_groups__add_client(username, group->groupname, priority, false); } } } @@ -290,9 +294,9 @@ int dynsec_groups__config_load(cJSON *tree) } HASH_SORT(local_groups, group_cmp); - j_group = cJSON_GetObjectItem(tree, "anonymousGroup"); - if(j_group && cJSON_IsString(j_group)){ - dynsec_anonymous_group = dynsec_groups__find(j_group->valuestring); + json_get_string(tree, "anonymousGroup", &groupname, false); + if(groupname){ + dynsec_anonymous_group = dynsec_groups__find(groupname); } return 0; @@ -922,10 +926,12 @@ int dynsec_groups__process_modify(cJSON *j_responses, struct mosquitto *context, struct dynsec__group *group = NULL; struct dynsec__rolelist *rolelist = NULL; bool have_text_name = false, have_text_description = false, have_rolelist = false; - char *str; int rc; int priority; - cJSON *j_client, *j_clients, *jtmp; + cJSON *j_client, *j_clients; + char *username; + char *textname; + char *textdescription; const char *admin_clientid, *admin_username; if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){ @@ -943,9 +949,9 @@ int dynsec_groups__process_modify(cJSON *j_responses, struct mosquitto *context, return MOSQ_ERR_INVAL; } - if(json_get_string(command, "textname", &str, false) == MOSQ_ERR_SUCCESS){ + if(json_get_string(command, "textname", &textname, false) == MOSQ_ERR_SUCCESS){ have_text_name = true; - text_name = mosquitto_strdup(str); + text_name = mosquitto_strdup(textname); if(text_name == NULL){ dynsec__command_reply(j_responses, context, "modifyGroup", "Internal error", correlation_data); rc = MOSQ_ERR_NOMEM; @@ -953,9 +959,9 @@ int dynsec_groups__process_modify(cJSON *j_responses, struct mosquitto *context, } } - if(json_get_string(command, "textdescription", &str, false) == MOSQ_ERR_SUCCESS){ + if(json_get_string(command, "textdescription", &textdescription, false) == MOSQ_ERR_SUCCESS){ have_text_description = true; - text_description = mosquitto_strdup(str); + text_description = mosquitto_strdup(textdescription); if(text_description == NULL){ dynsec__command_reply(j_responses, context, "modifyGroup", "Internal error", correlation_data); rc = MOSQ_ERR_NOMEM; @@ -989,9 +995,9 @@ int dynsec_groups__process_modify(cJSON *j_responses, struct mosquitto *context, /* Iterate over array to check clients are valid before proceeding */ cJSON_ArrayForEach(j_client, j_clients){ if(cJSON_IsObject(j_client)){ - jtmp = cJSON_GetObjectItem(j_client, "username"); - if(jtmp && cJSON_IsString(jtmp)){ - client = dynsec_clients__find(jtmp->valuestring); + json_get_string(j_client, "username", &username, false); + if(username){ + client = dynsec_clients__find(username); if(client == NULL){ dynsec__command_reply(j_responses, context, "modifyGroup", "'clients' contains an object with a 'username' that does not exist", correlation_data); rc = MOSQ_ERR_INVAL; @@ -1012,10 +1018,10 @@ int dynsec_groups__process_modify(cJSON *j_responses, struct mosquitto *context, /* Now we can add the new clients to the group */ cJSON_ArrayForEach(j_client, j_clients){ if(cJSON_IsObject(j_client)){ - jtmp = cJSON_GetObjectItem(j_client, "username"); - if(jtmp && cJSON_IsString(jtmp)){ + json_get_string(j_client, "username", &username, false); + if(username){ json_get_int(j_client, "priority", &priority, true, -1); - dynsec_groups__add_client(jtmp->valuestring, groupname, priority, false); + dynsec_groups__add_client(username, groupname, priority, false); } } } diff --git a/plugins/dynamic-security/plugin.c b/plugins/dynamic-security/plugin.c index b8943bd5..b5b81fc1 100644 --- a/plugins/dynamic-security/plugin.c +++ b/plugins/dynamic-security/plugin.c @@ -321,8 +321,7 @@ static int dynsec_control_callback(int event, void *event_data, void *userdata) static int dynsec__process_set_default_acl_access(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { - cJSON *j_actions, *j_action, *j_acltype, *j_allow; - bool allow; + cJSON *j_actions, *j_action; const char *admin_clientid, *admin_username; j_actions = cJSON_GetObjectItem(command, "acls"); @@ -335,24 +334,23 @@ static int dynsec__process_set_default_acl_access(cJSON *j_responses, struct mos admin_username = mosquitto_client_username(context); cJSON_ArrayForEach(j_action, j_actions){ - j_acltype = cJSON_GetObjectItem(j_action, "acltype"); - j_allow = cJSON_GetObjectItem(j_action, "allow"); - if(j_acltype && cJSON_IsString(j_acltype) - && j_allow && cJSON_IsBool(j_allow)){ + char *acltype; + bool allow; - allow = cJSON_IsTrue(j_allow); + if(json_get_string(j_action, "acltype", &acltype, false) == MOSQ_ERR_SUCCESS + && json_get_bool(j_action, "allow", &allow, false, false) == MOSQ_ERR_SUCCESS){ - if(!strcasecmp(j_acltype->valuestring, ACL_TYPE_PUB_C_SEND)){ + if(!strcasecmp(acltype, ACL_TYPE_PUB_C_SEND)){ default_access.publish_c_send = allow; - }else if(!strcasecmp(j_acltype->valuestring, ACL_TYPE_PUB_C_RECV)){ + }else if(!strcasecmp(acltype, ACL_TYPE_PUB_C_RECV)){ default_access.publish_c_recv = allow; - }else if(!strcasecmp(j_acltype->valuestring, ACL_TYPE_SUB_GENERIC)){ + }else if(!strcasecmp(acltype, ACL_TYPE_SUB_GENERIC)){ default_access.subscribe = allow; - }else if(!strcasecmp(j_acltype->valuestring, ACL_TYPE_UNSUB_GENERIC)){ + }else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_GENERIC)){ default_access.unsubscribe = allow; } mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | setDefaultACLAccess | acltype=%s | allow=%s", - admin_clientid, admin_username, j_acltype->valuestring, allow?"true":"false"); + admin_clientid, admin_username, acltype, allow?"true":"false"); } } diff --git a/plugins/dynamic-security/rolelist.c b/plugins/dynamic-security/rolelist.c index 2bc1f163..63654c6b 100644 --- a/plugins/dynamic-security/rolelist.c +++ b/plugins/dynamic-security/rolelist.c @@ -164,7 +164,7 @@ int dynsec_rolelist__group_add(struct dynsec__group *group, struct dynsec__role int dynsec_rolelist__load_from_json(cJSON *command, struct dynsec__rolelist **rolelist) { - cJSON *j_roles, *j_role, *j_rolename; + cJSON *j_roles, *j_role; int priority; struct dynsec__role *role; @@ -172,10 +172,11 @@ int dynsec_rolelist__load_from_json(cJSON *command, struct dynsec__rolelist **ro if(j_roles){ if(cJSON_IsArray(j_roles)){ cJSON_ArrayForEach(j_role, j_roles){ - j_rolename = cJSON_GetObjectItem(j_role, "rolename"); - if(j_rolename && cJSON_IsString(j_rolename)){ + char *rolename; + json_get_string(j_role, "rolename", &rolename, false); + if(rolename){ json_get_int(j_role, "priority", &priority, true, -1); - role = dynsec_roles__find(j_rolename->valuestring); + role = dynsec_roles__find(rolename); if(role){ dynsec_rolelist__add(rolelist, role, priority); }else{ diff --git a/plugins/dynamic-security/roles.c b/plugins/dynamic-security/roles.c index 6a393bc0..13f0a6bd 100644 --- a/plugins/dynamic-security/roles.c +++ b/plugins/dynamic-security/roles.c @@ -215,12 +215,13 @@ static int insert_acl_cmp(struct dynsec__acl *a, struct dynsec__acl *b) static int dynsec_roles__acl_load(cJSON *j_acls, const char *key, struct dynsec__acl **acllist) { - cJSON *j_acl, *j_type, *jtmp; + cJSON *j_acl; struct dynsec__acl *acl; cJSON_ArrayForEach(j_acl, j_acls){ - j_type = cJSON_GetObjectItem(j_acl, "acltype"); - if(j_type == NULL || !cJSON_IsString(j_type) || strcasecmp(j_type->valuestring, key) != 0){ + char *acltype; + json_get_string(j_acl, "acltype", &acltype, false); + if(!acltype || strcasecmp(acltype, key) != 0){ continue; } acl = mosquitto_calloc(1, sizeof(struct dynsec__acl)); @@ -231,14 +232,14 @@ static int dynsec_roles__acl_load(cJSON *j_acls, const char *key, struct dynsec_ json_get_int(j_acl, "priority", &acl->priority, true, 0); json_get_bool(j_acl, "allow", &acl->allow, true, false); - jtmp = cJSON_GetObjectItem(j_acl, "allow"); - if(jtmp && cJSON_IsBool(jtmp)){ - acl->allow = cJSON_IsTrue(jtmp); + bool allow; + if(json_get_bool(j_acl, "allow", &allow, false, false) == MOSQ_ERR_SUCCESS){ + acl->allow = allow; } - jtmp = cJSON_GetObjectItem(j_acl, "topic"); - if(jtmp && cJSON_IsString(jtmp)){ - acl->topic = mosquitto_strdup(jtmp->valuestring); + char *topic; + if(json_get_string(j_acl, "topic", &topic, false) == MOSQ_ERR_SUCCESS){ + acl->topic = mosquitto_strdup(topic); } if(acl->topic == NULL){ @@ -255,7 +256,7 @@ static int dynsec_roles__acl_load(cJSON *j_acls, const char *key, struct dynsec_ int dynsec_roles__config_load(cJSON *tree) { - cJSON *j_roles, *j_role, *jtmp, *j_acls; + cJSON *j_roles, *j_role, *j_acls; struct dynsec__role *role; j_roles = cJSON_GetObjectItem(tree, "roles"); @@ -275,21 +276,21 @@ int dynsec_roles__config_load(cJSON *tree) } /* Role name */ - jtmp = cJSON_GetObjectItem(j_role, "rolename"); - if(jtmp == NULL){ + char *rolename; + if(json_get_string(j_role, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ mosquitto_free(role); continue; } - role->rolename = mosquitto_strdup(jtmp->valuestring); + role->rolename = mosquitto_strdup(rolename); if(role->rolename == NULL){ mosquitto_free(role); continue; } /* Text name */ - jtmp = cJSON_GetObjectItem(j_role, "textname"); - if(jtmp != NULL){ - role->text_name = mosquitto_strdup(jtmp->valuestring); + char *textname; + if(json_get_string(j_role, "textname", &textname, false) == MOSQ_ERR_SUCCESS){ + role->text_name = mosquitto_strdup(textname); if(role->text_name == NULL){ mosquitto_free(role->rolename); mosquitto_free(role); @@ -298,9 +299,9 @@ int dynsec_roles__config_load(cJSON *tree) } /* Text description */ - jtmp = cJSON_GetObjectItem(j_role, "textdescription"); - if(jtmp != NULL){ - role->text_description = mosquitto_strdup(jtmp->valuestring); + char *textdescription; + if(json_get_string(j_role, "textdescription", &textdescription, false) == MOSQ_ERR_SUCCESS){ + role->text_description = mosquitto_strdup(textdescription); if(role->text_description == NULL){ mosquitto_free(role->text_name); mosquitto_free(role->rolename); @@ -594,9 +595,9 @@ int dynsec_roles__process_add_acl(cJSON *j_responses, struct mosquitto *context, char *rolename; char *topic; struct dynsec__role *role; - cJSON *jtmp, *j_acltype; struct dynsec__acl **acllist, *acl; int rc; + char *acltype; const char *admin_clientid, *admin_username; if(json_get_string(command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ @@ -614,44 +615,37 @@ int dynsec_roles__process_add_acl(cJSON *j_responses, struct mosquitto *context, return MOSQ_ERR_SUCCESS; } - j_acltype = cJSON_GetObjectItem(command, "acltype"); - if(j_acltype == NULL || !cJSON_IsString(j_acltype)){ + if(json_get_string(command, "acltype", &acltype, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addRoleACL", "Invalid/missing acltype", correlation_data); return MOSQ_ERR_SUCCESS; } - if(!strcasecmp(j_acltype->valuestring, ACL_TYPE_PUB_C_SEND)){ + if(!strcasecmp(acltype, ACL_TYPE_PUB_C_SEND)){ acllist = &role->acls.publish_c_send; - }else if(!strcasecmp(j_acltype->valuestring, ACL_TYPE_PUB_C_RECV)){ + }else if(!strcasecmp(acltype, ACL_TYPE_PUB_C_RECV)){ acllist = &role->acls.publish_c_recv; - }else if(!strcasecmp(j_acltype->valuestring, ACL_TYPE_SUB_LITERAL)){ + }else if(!strcasecmp(acltype, ACL_TYPE_SUB_LITERAL)){ acllist = &role->acls.subscribe_literal; - }else if(!strcasecmp(j_acltype->valuestring, ACL_TYPE_SUB_PATTERN)){ + }else if(!strcasecmp(acltype, ACL_TYPE_SUB_PATTERN)){ acllist = &role->acls.subscribe_pattern; - }else if(!strcasecmp(j_acltype->valuestring, ACL_TYPE_UNSUB_LITERAL)){ + }else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_LITERAL)){ acllist = &role->acls.unsubscribe_literal; - }else if(!strcasecmp(j_acltype->valuestring, ACL_TYPE_UNSUB_PATTERN)){ + }else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_PATTERN)){ acllist = &role->acls.unsubscribe_pattern; }else{ dynsec__command_reply(j_responses, context, "addRoleACL", "Unknown acltype", correlation_data); return MOSQ_ERR_SUCCESS; } - jtmp = cJSON_GetObjectItem(command, "topic"); - if(jtmp && cJSON_IsString(jtmp)){ - if(mosquitto_validate_utf8(jtmp->valuestring, (int)strlen(jtmp->valuestring)) != MOSQ_ERR_SUCCESS){ + if(json_get_string(command, "topic", &topic, false) == MOSQ_ERR_SUCCESS){ + if(mosquitto_validate_utf8(topic, (int)strlen(topic)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addRoleACL", "Topic not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } - rc = mosquitto_sub_topic_check(jtmp->valuestring); + rc = mosquitto_sub_topic_check(topic); if(rc != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addRoleACL", "Invalid ACL topic", correlation_data); return MOSQ_ERR_INVAL; } - topic = mosquitto_strdup(jtmp->valuestring); - if(topic == NULL){ - dynsec__command_reply(j_responses, context, "addRoleACL", "Internal error", correlation_data); - return MOSQ_ERR_SUCCESS; - } }else{ dynsec__command_reply(j_responses, context, "addRoleACL", "Invalid/missing topic", correlation_data); return MOSQ_ERR_SUCCESS; @@ -659,18 +653,21 @@ int dynsec_roles__process_add_acl(cJSON *j_responses, struct mosquitto *context, HASH_FIND(hh, *acllist, topic, strlen(topic), acl); if(acl){ - mosquitto_free(topic); dynsec__command_reply(j_responses, context, "addRoleACL", "ACL with this topic already exists", correlation_data); return MOSQ_ERR_SUCCESS; } acl = mosquitto_calloc(1, sizeof(struct dynsec__acl)); if(acl == NULL){ - mosquitto_free(topic); dynsec__command_reply(j_responses, context, "addRoleACL", "Internal error", correlation_data); return MOSQ_ERR_SUCCESS; } - acl->topic = topic; + acl->topic = mosquitto_strdup(topic); + if(acl->topic == NULL){ + mosquitto_free(acl); + dynsec__command_reply(j_responses, context, "addRoleACL", "Internal error", correlation_data); + return MOSQ_ERR_SUCCESS; + } json_get_int(command, "priority", &acl->priority, true, 0); json_get_bool(command, "allow", &acl->allow, true, false); @@ -684,7 +681,7 @@ int dynsec_roles__process_add_acl(cJSON *j_responses, struct mosquitto *context, admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | addRoleACL | rolename=%s | acltype=%s | topic=%s | priority=%d | allow=%s", - admin_clientid, admin_username, rolename, j_acltype->valuestring, topic, acl->priority, acl->allow?"true":"false"); + admin_clientid, admin_username, rolename, acltype, topic, acl->priority, acl->allow?"true":"false"); return MOSQ_ERR_SUCCESS; } @@ -696,7 +693,7 @@ int dynsec_roles__process_remove_acl(cJSON *j_responses, struct mosquitto *conte struct dynsec__role *role; struct dynsec__acl **acllist, *acl; char *topic; - cJSON *j_acltype; + char *acltype; int rc; const char *admin_clientid, *admin_username; @@ -715,22 +712,21 @@ int dynsec_roles__process_remove_acl(cJSON *j_responses, struct mosquitto *conte return MOSQ_ERR_SUCCESS; } - j_acltype = cJSON_GetObjectItem(command, "acltype"); - if(j_acltype == NULL || !cJSON_IsString(j_acltype)){ + if(json_get_string(command, "acltype", &acltype, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "removeRoleACL", "Invalid/missing acltype", correlation_data); return MOSQ_ERR_SUCCESS; } - if(!strcasecmp(j_acltype->valuestring, ACL_TYPE_PUB_C_SEND)){ + if(!strcasecmp(acltype, ACL_TYPE_PUB_C_SEND)){ acllist = &role->acls.publish_c_send; - }else if(!strcasecmp(j_acltype->valuestring, ACL_TYPE_PUB_C_RECV)){ + }else if(!strcasecmp(acltype, ACL_TYPE_PUB_C_RECV)){ acllist = &role->acls.publish_c_recv; - }else if(!strcasecmp(j_acltype->valuestring, ACL_TYPE_SUB_LITERAL)){ + }else if(!strcasecmp(acltype, ACL_TYPE_SUB_LITERAL)){ acllist = &role->acls.subscribe_literal; - }else if(!strcasecmp(j_acltype->valuestring, ACL_TYPE_SUB_PATTERN)){ + }else if(!strcasecmp(acltype, ACL_TYPE_SUB_PATTERN)){ acllist = &role->acls.subscribe_pattern; - }else if(!strcasecmp(j_acltype->valuestring, ACL_TYPE_UNSUB_LITERAL)){ + }else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_LITERAL)){ acllist = &role->acls.unsubscribe_literal; - }else if(!strcasecmp(j_acltype->valuestring, ACL_TYPE_UNSUB_PATTERN)){ + }else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_PATTERN)){ acllist = &role->acls.unsubscribe_pattern; }else{ dynsec__command_reply(j_responses, context, "removeRoleACL", "Unknown acltype", correlation_data); @@ -762,7 +758,7 @@ int dynsec_roles__process_remove_acl(cJSON *j_responses, struct mosquitto *conte admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | removeRoleACL | rolename=%s | acltype=%s | topic=%s", - admin_clientid, admin_username, rolename, j_acltype->valuestring, topic); + admin_clientid, admin_username, rolename, acltype, topic); }else{ dynsec__command_reply(j_responses, context, "removeRoleACL", "ACL not found", correlation_data); From b76c3c7820d9e910486d29686fc78e4c047975c3 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 13 Jun 2023 17:07:43 +0100 Subject: [PATCH 62/79] Dynsec: Don't allow duplicate c/g/r when loading config --- ChangeLog.txt | 2 ++ plugins/dynamic-security/clients.c | 20 +++++++++++------- plugins/dynamic-security/groups.c | 14 ++++++++----- plugins/dynamic-security/roles.c | 33 +++++++++++++++++++----------- 4 files changed, 45 insertions(+), 24 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 816ba8eb..45adc1da 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -28,6 +28,8 @@ Broker: - Fix any possible case where a json string might be incorrectly loaded. This could have caused a crash if a textname or textdescription field of a role was not a string, when loading the dynsec config from file only. +- Dynsec plugin will not allow duplicate clients/groups/roles when loading + config from file, which matches the behaviour for when creating them. Client library: - Use CLOCK_BOOTTIME when available, to keep track of time. This solves the diff --git a/plugins/dynamic-security/clients.c b/plugins/dynamic-security/clients.c index 07ae7a42..c21d5696 100644 --- a/plugins/dynamic-security/clients.c +++ b/plugins/dynamic-security/clients.c @@ -124,18 +124,24 @@ int dynsec_clients__config_load(cJSON *tree) cJSON_ArrayForEach(j_client, j_clients){ if(cJSON_IsObject(j_client) == true){ + /* Username */ + char *username; + json_get_string(j_client, "username", &username, false); + if(!username){ + continue; + } + + client = dynsec_clients__find(username); + if(client){ + continue; + } + client = mosquitto_calloc(1, sizeof(struct dynsec__client)); if(client == NULL){ return MOSQ_ERR_NOMEM; } - /* Username */ - char *username; - json_get_string(j_client, "username", &username, false); - if(!username){ - mosquitto_free(client); - continue; - } + client->username = mosquitto_strdup(username); if(client->username == NULL){ mosquitto_free(client); diff --git a/plugins/dynamic-security/groups.c b/plugins/dynamic-security/groups.c index 601b06d1..16fa76f3 100644 --- a/plugins/dynamic-security/groups.c +++ b/plugins/dynamic-security/groups.c @@ -214,16 +214,20 @@ int dynsec_groups__config_load(cJSON *tree) cJSON_ArrayForEach(j_group, j_groups){ if(cJSON_IsObject(j_group) == true){ + /* Group name */ + if(json_get_string(j_group, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){ + continue; + } + group = dynsec_groups__find(groupname); + if(group){ + continue; + } + group = mosquitto_calloc(1, sizeof(struct dynsec__group)); if(group == NULL){ return MOSQ_ERR_NOMEM; } - /* Group name */ - if(json_get_string(j_group, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){ - mosquitto_free(group); - continue; - } group->groupname = strdup(groupname); if(group->groupname == NULL){ mosquitto_free(group); diff --git a/plugins/dynamic-security/roles.c b/plugins/dynamic-security/roles.c index 13f0a6bd..09b9fe3c 100644 --- a/plugins/dynamic-security/roles.c +++ b/plugins/dynamic-security/roles.c @@ -220,10 +220,19 @@ static int dynsec_roles__acl_load(cJSON *j_acls, const char *key, struct dynsec_ cJSON_ArrayForEach(j_acl, j_acls){ char *acltype; + char *topic; + json_get_string(j_acl, "acltype", &acltype, false); - if(!acltype || strcasecmp(acltype, key) != 0){ + json_get_string(j_acl, "topic", &topic, false); + + if(!acltype || strcasecmp(acltype, key) != 0 || !topic){ continue; } + HASH_FIND(hh, *acllist, topic, strlen(topic), acl); + if(acl){ + continue; + } + acl = mosquitto_calloc(1, sizeof(struct dynsec__acl)); if(acl == NULL){ return 1; @@ -237,11 +246,7 @@ static int dynsec_roles__acl_load(cJSON *j_acls, const char *key, struct dynsec_ acl->allow = allow; } - char *topic; - if(json_get_string(j_acl, "topic", &topic, false) == MOSQ_ERR_SUCCESS){ - acl->topic = mosquitto_strdup(topic); - } - + acl->topic = mosquitto_strdup(topic); if(acl->topic == NULL){ mosquitto_free(acl); continue; @@ -270,17 +275,21 @@ int dynsec_roles__config_load(cJSON *tree) cJSON_ArrayForEach(j_role, j_roles){ if(cJSON_IsObject(j_role) == true){ + /* Role name */ + char *rolename; + if(json_get_string(j_role, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ + continue; + } + role = dynsec_roles__find(rolename); + if(role){ + continue; + } + role = mosquitto_calloc(1, sizeof(struct dynsec__role)); if(role == NULL){ return MOSQ_ERR_NOMEM; } - /* Role name */ - char *rolename; - if(json_get_string(j_role, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ - mosquitto_free(role); - continue; - } role->rolename = mosquitto_strdup(rolename); if(role->rolename == NULL){ mosquitto_free(role); From 284db04bc305830a32483b7a3505338fd2f3257a Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Wed, 14 Jun 2023 15:15:33 +0100 Subject: [PATCH 63/79] Library will no longer allow single level wildcard certificates, e.g. *.com --- .gitignore | 1 + ChangeLog.txt | 3 +- lib/tls_mosq.c | 11 +++++ test/unit/Makefile | 10 ++++- test/unit/tls_stubs.c | 40 +++++++++++++++++ test/unit/tls_test.c | 102 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 165 insertions(+), 2 deletions(-) create mode 100644 test/unit/tls_stubs.c create mode 100644 test/unit/tls_test.c diff --git a/.gitignore b/.gitignore index 5694e123..d12fdf42 100644 --- a/.gitignore +++ b/.gitignore @@ -81,6 +81,7 @@ test/unit/mosq_test test/unit/persist_read_test test/unit/persist_write_test test/unit/subs_test +test/unit/tls_test test/unit/out/ www/cache/ diff --git a/ChangeLog.txt b/ChangeLog.txt index 45adc1da..6ad030a9 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -5,7 +5,8 @@ Security: - Broker will now reject Will messages that attempt to publish to $CONTROL/. - Broker now validates usernames provided in a TLS certificate or TLS-PSK identity are valid UTF-8. -- Fix potential crash when loading invalid persitence file. +- Fix potential crash when loading invalid persistence file. +- Library will no longer allow single level wildcard certificates, e.g. *.com Broker: - Fix $SYS messages being expired after 60 seconds and hence unchanged values diff --git a/lib/tls_mosq.c b/lib/tls_mosq.c index 940df073..f85379ed 100644 --- a/lib/tls_mosq.c +++ b/lib/tls_mosq.c @@ -105,6 +105,17 @@ static int mosquitto__cmp_hostname_wildcard(char *certname, const char *hostname break; } } + len = strlen(hostname); + int dotcount = 0; + for(i=0; i +#include + +int tls_ex_index_mosq; + +struct mosquitto_db{ + +}; + +int log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...) +{ + UNUSED(mosq); + UNUSED(priority); + UNUSED(fmt); + + return 0; +} + +time_t mosquitto_time(void) +{ + return 123; +} + +int net__socket_close(struct mosquitto_db *db, struct mosquitto *mosq) +{ + UNUSED(db); + UNUSED(mosq); + + return MOSQ_ERR_SUCCESS; +} + +int send__pingreq(struct mosquitto *mosq) +{ + UNUSED(mosq); + + return MOSQ_ERR_SUCCESS; +} + diff --git a/test/unit/tls_test.c b/test/unit/tls_test.c new file mode 100644 index 00000000..26dd36a3 --- /dev/null +++ b/test/unit/tls_test.c @@ -0,0 +1,102 @@ +#include +#include + +#define WITH_TLS + +#include "tls_mosq.c" + +//static int mosquitto__cmp_hostname_wildcard(char *certname, const char *hostname) + +void hostname_cmp_helper(char *certname, const char *hostname, int expected) +{ + int rc = mosquitto__cmp_hostname_wildcard(certname, hostname); + CU_ASSERT_EQUAL(rc, expected); + if(rc != expected){ + printf("%d || %d\n", rc, expected); + } +} + +void TEST_tls_hostname_compare_null(void) +{ + hostname_cmp_helper(NULL, "localhost", 1); + hostname_cmp_helper("localhost", NULL, 1); + hostname_cmp_helper(NULL, NULL, 1); +} + + +void TEST_tls_hostname_compare_simple(void) +{ + hostname_cmp_helper("localhost", "localhost", 0); + hostname_cmp_helper("localhost", "localhose", 15); +} + + +void TEST_tls_hostname_compare_bad_wildcard_format(void) +{ + hostname_cmp_helper("**localhost", "localhost", 1); + hostname_cmp_helper("*,localhost", "localhost", 1); + hostname_cmp_helper("*.", "localhost", 1); +} + + +void TEST_tls_hostname_compare_invalid_wildcard(void) +{ + hostname_cmp_helper("*.com", "example.com", 1); + hostname_cmp_helper("*.com", "example.org", 1); + hostname_cmp_helper("*.org", "example.org", 1); +} + + +void TEST_tls_hostname_compare_good_wildcard(void) +{ + hostname_cmp_helper("*.example.com", "test.example.com", 0); + hostname_cmp_helper("*.example.com", "test.example.org", -12); + hostname_cmp_helper("*.example.org", "test.example.org", 0); +} + + +/* ======================================================================== + * TEST SUITE SETUP + * ======================================================================== */ + + +int main(int argc, char *argv[]) +{ + CU_pSuite test_suite = NULL; + unsigned int fails; + + UNUSED(argc); + UNUSED(argv); + + if(CU_initialize_registry() != CUE_SUCCESS){ + printf("Error initializing CUnit registry.\n"); + return 1; + } + + test_suite = CU_add_suite("Subs", NULL, NULL); + if(!test_suite){ + printf("Error adding CUnit TLS test suite.\n"); + CU_cleanup_registry(); + return 1; + } + + if(0 + || !CU_add_test(test_suite, "TLS hostname compare null", TEST_tls_hostname_compare_null) + || !CU_add_test(test_suite, "TLS hostname compare simple", TEST_tls_hostname_compare_simple) + || !CU_add_test(test_suite, "TLS hostname compare bad wildcard format", TEST_tls_hostname_compare_bad_wildcard_format) + || !CU_add_test(test_suite, "TLS hostname compare invalid wildcard", TEST_tls_hostname_compare_invalid_wildcard) + || !CU_add_test(test_suite, "TLS hostname compare good wildcard", TEST_tls_hostname_compare_good_wildcard) + ){ + + printf("Error adding TLS CUnit tests.\n"); + CU_cleanup_registry(); + return 1; + } + + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + fails = CU_get_number_of_failures(); + CU_cleanup_registry(); + + return (int)fails; +} From 70d713ca07ba46ddfbebb4a569e342364844d6c2 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Wed, 14 Jun 2023 15:50:10 +0100 Subject: [PATCH 64/79] Fix heap overflow when reading corrupt config with "log_dest file". --- ChangeLog.txt | 1 + src/conf.c | 10 ++++++---- src/handle_connect.c | 1 + 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 6ad030a9..eef61746 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -31,6 +31,7 @@ Broker: not a string, when loading the dynsec config from file only. - Dynsec plugin will not allow duplicate clients/groups/roles when loading config from file, which matches the behaviour for when creating them. +- Fix heap overflow when reading corrupt config with "log_dest file". Client library: - Use CLOCK_BOOTTIME when available, to keep track of time. This solves the diff --git a/src/conf.c b/src/conf.c index a7b9b062..80c0cd82 100644 --- a/src/conf.c +++ b/src/conf.c @@ -1533,15 +1533,16 @@ static int config__read_file_core(struct mosquitto__config *config, bool reload, }else if(!strcmp(token, "dlt")){ cr->log_dest |= MQTT3_LOG_DLT; }else if(!strcmp(token, "file")){ - cr->log_dest |= MQTT3_LOG_FILE; if(config->log_fptr || config->log_file){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Duplicate \"log_dest file\" value."); return MOSQ_ERR_INVAL; } /* Get remaining string. */ - token = &token[strlen(token)+1]; - while(token[0] == ' ' || token[0] == '\t'){ - token++; + token = saveptr; + if(token && token[0]){ + while(token[0] == ' ' || token[0] == '\t'){ + token++; + } } if(token[0]){ config->log_file = mosquitto__strdup(token); @@ -1553,6 +1554,7 @@ static int config__read_file_core(struct mosquitto__config *config, bool reload, log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty \"log_dest file\" value in configuration."); return MOSQ_ERR_INVAL; } + cr->log_dest |= MQTT3_LOG_FILE; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid log_dest value (%s).", token); return MOSQ_ERR_INVAL; diff --git a/src/handle_connect.c b/src/handle_connect.c index 58bea7b3..cb22e358 100644 --- a/src/handle_connect.c +++ b/src/handle_connect.c @@ -951,6 +951,7 @@ int handle__connect(struct mosquitto *context) handle_connect_error: + mosquitto_property_free_all(&properties); mosquitto__free(auth_data); mosquitto__free(client_id); mosquitto__free(username); From 4f9002c58a02d2364ed93825009a659f178f5a45 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Sat, 1 Jul 2023 08:16:52 +0100 Subject: [PATCH 65/79] Fix UTF-8 control characters test --- test/unit/utf8.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/unit/utf8.c b/test/unit/utf8.c index c58e1529..a3f9508c 100644 --- a/test/unit/utf8.c +++ b/test/unit/utf8.c @@ -415,10 +415,10 @@ void TEST_utf8_control_characters(void) buf[1] = '\0'; utf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8); - /* U+007F to U+009F are two byte control characters */ + /* U+0080 to U+009F are two byte control characters */ for(i=0x80; i<0xA0; i++){ buf[0] = 0xC2; - buf[1] = (uint8_t)(i-0x80); + buf[1] = (uint8_t)i; buf[2] = '\0'; utf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8); } From 00b24e0eb0686e9a76feb71fdaee650cb7e612fa Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Fri, 7 Jul 2023 23:41:04 +0100 Subject: [PATCH 66/79] Fix memory leak when clients send v5 CONNECT packets. This occurs when they have a will message that contains invalid property types. --- ChangeLog.txt | 2 ++ src/property_broker.c | 1 + 2 files changed, 3 insertions(+) diff --git a/ChangeLog.txt b/ChangeLog.txt index eef61746..93f8503c 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -2,6 +2,8 @@ Security: - CVE-2023-28366: Fix memory leak in broker when clients send multiple QoS 2 messages with the same message ID, but then never respond to the PUBREC commands. +- CVE-xxxx-xxxxx: Fix memory leak when clients send v5 CONNECT packets with a + will message that contains invalid property types. - Broker will now reject Will messages that attempt to publish to $CONTROL/. - Broker now validates usernames provided in a TLS certificate or TLS-PSK identity are valid UTF-8. diff --git a/src/property_broker.c b/src/property_broker.c index e829b300..a0c856a9 100644 --- a/src/property_broker.c +++ b/src/property_broker.c @@ -103,6 +103,7 @@ int property__process_will(struct mosquitto *context, struct mosquitto_message_a break; default: + msg->properties = msg_properties; return MOSQ_ERR_PROTOCOL; break; } From 3ab0a9a3fd0a07ea93ba937d8d4da236d32548ee Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Mon, 10 Jul 2023 00:01:10 +0100 Subject: [PATCH 67/79] mosquitto_ctrl dynsec init uses open( , O_EXCL | O_CREAT) This allows us to refuse to open an existing file, without a race condition. --- ChangeLog.txt | 2 ++ apps/mosquitto_ctrl/dynsec.c | 19 ++++++++++++------- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 93f8503c..df350ed2 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -48,6 +48,8 @@ Clients: Apps: - mosquitto_passwd uses mkstemp() for backup files. +- `mosquitto_ctrl dynsec init` will refuse to overwrite an existing file, + without a race-condition. 2.0.15 - 2022-08-16 diff --git a/apps/mosquitto_ctrl/dynsec.c b/apps/mosquitto_ctrl/dynsec.c index 8e5dd726..929217af 100644 --- a/apps/mosquitto_ctrl/dynsec.c +++ b/apps/mosquitto_ctrl/dynsec.c @@ -23,6 +23,8 @@ Contributors: #include #ifndef WIN32 +# include +# include # include #endif @@ -739,13 +741,6 @@ static int dynsec_init(int argc, char *argv[]) admin_password = password; } - fptr = mosquitto__fopen(filename, "rb", true); - if(fptr){ - fclose(fptr); - fprintf(stderr, "dynsec init: '%s' already exists. Remove the file or use a different location..\n", filename); - return -1; - } - tree = init_create(admin_user, admin_password, "admin"); if(tree == NULL){ fprintf(stderr, "dynsec init: Out of memory.\n"); @@ -754,7 +749,17 @@ static int dynsec_init(int argc, char *argv[]) json_str = cJSON_Print(tree); cJSON_Delete(tree); +#ifdef WIN32 fptr = mosquitto__fopen(filename, "wb", true); +#else + int fd = open(filename, O_CREAT | O_EXCL | O_WRONLY, 0640); + if(fd < 0){ + free(json_str); + fprintf(stderr, "dynsec init: Unable to open '%s' for writing (%s).\n", filename, strerror(errno)); + return -1; + } + fptr = fdopen(fd, "wb"); +#endif if(fptr){ fprintf(fptr, "%s", json_str); free(json_str); From 9109e9e0e238bf282f1c0a2222f5ec0aaa8a1eef Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 15 Aug 2023 16:19:39 +0100 Subject: [PATCH 68/79] Fix setlinebuf() on Windows --- src/logging.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/logging.c b/src/logging.c index a7e62443..0af2787f 100644 --- a/src/logging.c +++ b/src/logging.c @@ -131,7 +131,7 @@ int log__init(struct mosquitto__config *config) } } if(log_destinations & MQTT3_LOG_STDOUT){ - setlinebuf(stdout); + setvbuf(stdout, NULL, _IOLBF, 0); } #ifdef WITH_DLT if(log_destinations & MQTT3_LOG_DLT){ From 20df87460469fc860dd2750be692c01cc961b8bd Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 15 Aug 2023 16:30:11 +0100 Subject: [PATCH 69/79] Remove broken Travis links. Closes #2867 --- README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/README.md b/README.md index a0d275e8..c584394c 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,3 @@ Equivalent options for enabling/disabling features are available when using the ## Credits Mosquitto was written by Roger Light - -Master: [![Travis Build Status (master)](https://travis-ci.org/eclipse/mosquitto.svg?branch=master)](https://travis-ci.org/eclipse/mosquitto) -Develop: [![Travis Build Status (develop)](https://travis-ci.org/eclipse/mosquitto.svg?branch=develop)](https://travis-ci.org/eclipse/mosquitto) -Fixes: [![Travis Build Status (fixes)](https://travis-ci.org/eclipse/mosquitto.svg?branch=fixes)](https://travis-ci.org/eclipse/mosquitto) From 66faab12d5b558626923021466381098e4a615bf Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 15 Aug 2023 17:16:28 +0100 Subject: [PATCH 70/79] Document mosquitto_rr -W option Closes #2839. Thanks to KaeLL --- man/mosquitto_rr.1.xml | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/man/mosquitto_rr.1.xml b/man/mosquitto_rr.1.xml index 7e45b683..2563a73c 100644 --- a/man/mosquitto_rr.1.xml +++ b/man/mosquitto_rr.1.xml @@ -639,6 +639,16 @@ Defaults to . + + + + Provide a timeout as an integer number of seconds. + mosquitto_sub will stop processing messages and + disconnect after this number of seconds has + passed. The timeout starts just after the client has + connected to the broker. + + From 1f3b23cd60325fe3833e2c1418ce3fea6db824c6 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 15 Aug 2023 17:22:13 +0100 Subject: [PATCH 71/79] Fix broken links on security.md Closes #2855. Thanks to cfi-gb --- www/pages/security.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/www/pages/security.md b/www/pages/security.md index ddc4423f..94768ea5 100644 --- a/www/pages/security.md +++ b/www/pages/security.md @@ -65,14 +65,14 @@ can be found in the [security category]. inclusive, fixed in **1.4.12**. More details at [security-advisory-cve-2017-7650]. -[version-166-released]: /2019/09/version-1-6-6-released/ -[version-162-released]: /2019/04/version-1-6-2-released/ -[version-155-released]: /2018/11/version-155-released/ -[version-154-released]: /2018/11/version-154-released/ -[security-advisory-cve-2018-12543]: /2018/09/security-advisory-cve-2018-12543/ -[security-advisory-cve-2017-7651-cve-2017-7652]: /2018/02/security-advisory-cve-2017-7651-cve-2017-7652/ -[security-advisory-cve-2017-7650]: /2017/05/security-advisory-cve-2017-7650/ -[security-advisory-cve-2017-9868]: /2017/06/security-advisory-cve-2017-9868/ +[version-166-released]: /blog/2019/09/version-1-6-6-released/ +[version-162-released]: /blog/2019/04/version-1-6-2-released/ +[version-155-released]: /blog/2018/11/version-155-released/ +[version-154-released]: /blog/2018/11/version-154-released/ +[security-advisory-cve-2018-12543]: /blog/2018/09/security-advisory-cve-2018-12543/ +[security-advisory-cve-2017-7651-cve-2017-7652]: /blog/2018/02/security-advisory-cve-2017-7651-cve-2017-7652/ +[security-advisory-cve-2017-7650]: /blog/2017/05/security-advisory-cve-2017-7650/ +[security-advisory-cve-2017-9868]: /blog/2017/06/security-advisory-cve-2017-9868/ [Eclipse Security]: https://www.eclipse.org/security/ [security category]: /blog/categories/security/ @@ -84,9 +84,9 @@ can be found in the [security category]. [CVE-2018-20145]: https://nvd.nist.gov/vuln/detail/CVE-2018-20145 [CVE-2018-12543]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-12543 [CVE-2017-9868]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-9868 -[CVE-2017-7655]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7652 -[CVE-2017-7654]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7652 -[CVE-2017-7653]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7652 +[CVE-2017-7655]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7655 +[CVE-2017-7654]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7654 +[CVE-2017-7653]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7653 [CVE-2017-7652]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7652 [CVE-2017-7651]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7651 [CVE-2017-7650]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7650 From afba3ec9a082e97b1d99a16f82714c7d5de8f8ad Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 15 Aug 2023 17:40:36 +0100 Subject: [PATCH 72/79] Bump version, update changelog. --- CMakeLists.txt | 2 +- ChangeLog.txt | 7 ++++++- config.mk | 2 +- include/mosquitto.h | 2 +- installer/mosquitto.nsi | 2 +- installer/mosquitto64.nsi | 2 +- set-version.sh | 2 +- snap/snapcraft.yaml | 2 +- 8 files changed, 13 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b8913c2d..c14a47bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ cmake_minimum_required(VERSION 3.1) cmake_policy(SET CMP0042 NEW) project(mosquitto) -set (VERSION 2.0.15) +set (VERSION 2.0.16) list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/") diff --git a/ChangeLog.txt b/ChangeLog.txt index df350ed2..0c797358 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,8 +1,13 @@ +2.0.16 - 2023-08-16 +=================== + Security: - CVE-2023-28366: Fix memory leak in broker when clients send multiple QoS 2 messages with the same message ID, but then never respond to the PUBREC commands. -- CVE-xxxx-xxxxx: Fix memory leak when clients send v5 CONNECT packets with a +- CVE-2023-0809: Fix excessive memory being allocated based on malicious + initial packets that are not CONNECT packets. +- CVE-2023-3592: Fix memory leak when clients send v5 CONNECT packets with a will message that contains invalid property types. - Broker will now reject Will messages that attempt to publish to $CONTROL/. - Broker now validates usernames provided in a TLS certificate or TLS-PSK diff --git a/config.mk b/config.mk index 73daefdf..d64636bc 100644 --- a/config.mk +++ b/config.mk @@ -127,7 +127,7 @@ WITH_XTREPORT=no # Also bump lib/mosquitto.h, CMakeLists.txt, # installer/mosquitto.nsi, installer/mosquitto64.nsi -VERSION=2.0.15 +VERSION=2.0.16 # Client library SO version. Bump if incompatible API/ABI changes are made. SOVERSION=1 diff --git a/include/mosquitto.h b/include/mosquitto.h index 2cc64cef..6df18918 100644 --- a/include/mosquitto.h +++ b/include/mosquitto.h @@ -66,7 +66,7 @@ extern "C" { #define LIBMOSQUITTO_MAJOR 2 #define LIBMOSQUITTO_MINOR 0 -#define LIBMOSQUITTO_REVISION 15 +#define LIBMOSQUITTO_REVISION 16 /* LIBMOSQUITTO_VERSION_NUMBER looks like 1002001 for e.g. version 1.2.1. */ #define LIBMOSQUITTO_VERSION_NUMBER (LIBMOSQUITTO_MAJOR*1000000+LIBMOSQUITTO_MINOR*1000+LIBMOSQUITTO_REVISION) diff --git a/installer/mosquitto.nsi b/installer/mosquitto.nsi index 5450fae0..39659637 100644 --- a/installer/mosquitto.nsi +++ b/installer/mosquitto.nsi @@ -9,7 +9,7 @@ !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' Name "Eclipse Mosquitto" -!define VERSION 2.0.15 +!define VERSION 2.0.16 OutFile "mosquitto-${VERSION}-install-windows-x86.exe" InstallDir "$PROGRAMFILES\mosquitto" diff --git a/installer/mosquitto64.nsi b/installer/mosquitto64.nsi index 71d0aef9..5b7086bb 100644 --- a/installer/mosquitto64.nsi +++ b/installer/mosquitto64.nsi @@ -9,7 +9,7 @@ !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' Name "Eclipse Mosquitto" -!define VERSION 2.0.15 +!define VERSION 2.0.16 OutFile "mosquitto-${VERSION}-install-windows-x64.exe" !include "x64.nsh" diff --git a/set-version.sh b/set-version.sh index 81d01473..57c3bcb8 100755 --- a/set-version.sh +++ b/set-version.sh @@ -2,7 +2,7 @@ MAJOR=2 MINOR=0 -REVISION=15 +REVISION=16 sed -i "s/^VERSION=.*/VERSION=${MAJOR}.${MINOR}.${REVISION}/" config.mk diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index a330a4e5..177ee2ec 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: mosquitto -version: 2.0.15 +version: 2.0.16 summary: Eclipse Mosquitto MQTT broker description: This is a message broker that supports version 5.0, 3.1.1, and 3.1 of the MQTT protocol. From 166d1489f2c425f8b04bf1a25dd90e9724628eac Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Wed, 16 Aug 2023 14:05:47 +0100 Subject: [PATCH 73/79] Update docker --- docker/2.0-openssl/Dockerfile | 4 ++-- docker/2.0/Dockerfile | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/2.0-openssl/Dockerfile b/docker/2.0-openssl/Dockerfile index 1cd4c96c..3f9ea89e 100644 --- a/docker/2.0-openssl/Dockerfile +++ b/docker/2.0-openssl/Dockerfile @@ -3,8 +3,8 @@ FROM alpine:3.18 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" -ENV VERSION=2.0.15 \ - DOWNLOAD_SHA256=4735b1d32e3f91c7a8896741d88a3022e89730a1ee897946decfa0df27039ac6 \ +ENV VERSION=2.0.16 \ + DOWNLOAD_SHA256=638db9e43e89d243ea98d10d66e76ff376131217780decd01abaffacc66d8035 \ GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7 \ LWS_VERSION=4.2.1 \ LWS_SHA256=842da21f73ccba2be59e680de10a8cce7928313048750eb6ad73b6fa50763c51 diff --git a/docker/2.0/Dockerfile b/docker/2.0/Dockerfile index d58983ed..03ed59d4 100644 --- a/docker/2.0/Dockerfile +++ b/docker/2.0/Dockerfile @@ -3,8 +3,8 @@ FROM alpine:3.18 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" -ENV VERSION=2.0.15 \ - DOWNLOAD_SHA256=4735b1d32e3f91c7a8896741d88a3022e89730a1ee897946decfa0df27039ac6 \ +ENV VERSION=2.0.16 \ + DOWNLOAD_SHA256=638db9e43e89d243ea98d10d66e76ff376131217780decd01abaffacc66d8035 \ GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7 \ LWS_VERSION=4.2.1 \ LWS_SHA256=842da21f73ccba2be59e680de10a8cce7928313048750eb6ad73b6fa50763c51 From bfb373d774d8530e8d6620776304a3e0b0201793 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 22 Aug 2023 07:40:54 +0100 Subject: [PATCH 74/79] Fix `max_queued_message 0` stopping clients from receiving messages. Closes #2789. Thanks to Daedaluz. --- ChangeLog.txt | 8 ++++++++ lib/packet_mosq.c | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 0c797358..2660dc8b 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,11 @@ +2.0.16 - 2023-08-22 +=================== + +Broker: +- Fix `max_queued_message 0` stopping clients from receiving messages. + Closes #2879. + + 2.0.16 - 2023-08-16 =================== diff --git a/lib/packet_mosq.c b/lib/packet_mosq.c index 8c06b942..fd716baf 100644 --- a/lib/packet_mosq.c +++ b/lib/packet_mosq.c @@ -154,7 +154,7 @@ int packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet) pthread_mutex_lock(&mosq->out_packet_mutex); #ifdef WITH_BROKER - if(mosq->out_packet_count >= db.config->max_queued_messages){ + if(db.config->max_queued_messages > 0 && mosq->out_packet_count >= db.config->max_queued_messages){ mosquitto__free(packet); if(mosq->is_dropping == false){ mosq->is_dropping = true; From 47ae6dd0e28365462e69bde125fd7bf5a3d08f34 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 22 Aug 2023 08:09:36 +0100 Subject: [PATCH 75/79] Fix `mosquitto_passwd -U` backup file creation. Closes #2873. --- ChangeLog.txt | 3 +++ apps/mosquitto_passwd/mosquitto_passwd.c | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index 2660dc8b..a64e7548 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -5,6 +5,9 @@ Broker: - Fix `max_queued_message 0` stopping clients from receiving messages. Closes #2879. +Apps: +- Fix `mosquitto_passwd -U` backup file creation. Closes #2873. + 2.0.16 - 2023-08-16 =================== diff --git a/apps/mosquitto_passwd/mosquitto_passwd.c b/apps/mosquitto_passwd/mosquitto_passwd.c index 262f89cf..a4d271a3 100644 --- a/apps/mosquitto_passwd/mosquitto_passwd.c +++ b/apps/mosquitto_passwd/mosquitto_passwd.c @@ -629,13 +629,14 @@ int main(int argc, char *argv[]) return 1; } - backup_file = malloc((size_t)strlen(password_file)+strlen(".backup.XXXXXX")); + size_t len = strlen(password_file) + strlen(".backup.XXXXXX") + 1; + backup_file = malloc(len); if(!backup_file){ fprintf(stderr, "Error: Out of memory.\n"); free(password_file); return 1; } - snprintf(backup_file, strlen(password_file)+5, "%s.backup.XXXXXX", password_file); + snprintf(backup_file, len, "%s.backup.XXXXXX", password_file); free(password_file); password_file = NULL; From 28d96d8ebca9f6bdb7f272f1095760953e62d828 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 22 Aug 2023 08:39:40 +0100 Subject: [PATCH 76/79] Fix `max_inflight_messages` not being set correctly. Closes #2876. --- ChangeLog.txt | 5 +++-- src/context.c | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index a64e7548..7976dbd3 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,9 +1,10 @@ -2.0.16 - 2023-08-22 +2.0.17 - 2023-08-22 =================== Broker: -- Fix `max_queued_message 0` stopping clients from receiving messages. +- Fix `max_queued_messages 0` stopping clients from receiving messages. Closes #2879. +- Fix `max_inflight_messages` not being set correctly. Closes #2876. Apps: - Fix `mosquitto_passwd -U` backup file creation. Closes #2873. diff --git a/src/context.c b/src/context.c index 3ed49765..837be458 100644 --- a/src/context.c +++ b/src/context.c @@ -83,9 +83,9 @@ struct mosquitto *context__init(mosq_sock_t sock) } } context->bridge = NULL; - context->msgs_in.inflight_maximum = 1; + context->msgs_in.inflight_maximum = db.config->max_inflight_messages; + context->msgs_in.inflight_quota = db.config->max_inflight_messages; context->msgs_out.inflight_maximum = db.config->max_inflight_messages; - context->msgs_in.inflight_quota = 1; context->msgs_out.inflight_quota = db.config->max_inflight_messages; context->max_qos = 2; #ifdef WITH_TLS From 597d64b7fb89a7941f3d60c5236f25d020120372 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 22 Aug 2023 08:40:19 +0100 Subject: [PATCH 77/79] Bump version. --- CMakeLists.txt | 2 +- config.mk | 2 +- include/mosquitto.h | 2 +- installer/mosquitto.nsi | 2 +- installer/mosquitto64.nsi | 2 +- set-version.sh | 2 +- snap/snapcraft.yaml | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c14a47bd..8d337c9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ cmake_minimum_required(VERSION 3.1) cmake_policy(SET CMP0042 NEW) project(mosquitto) -set (VERSION 2.0.16) +set (VERSION 2.0.17) list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/") diff --git a/config.mk b/config.mk index d64636bc..b8bcf86d 100644 --- a/config.mk +++ b/config.mk @@ -127,7 +127,7 @@ WITH_XTREPORT=no # Also bump lib/mosquitto.h, CMakeLists.txt, # installer/mosquitto.nsi, installer/mosquitto64.nsi -VERSION=2.0.16 +VERSION=2.0.17 # Client library SO version. Bump if incompatible API/ABI changes are made. SOVERSION=1 diff --git a/include/mosquitto.h b/include/mosquitto.h index 6df18918..2f3c6384 100644 --- a/include/mosquitto.h +++ b/include/mosquitto.h @@ -66,7 +66,7 @@ extern "C" { #define LIBMOSQUITTO_MAJOR 2 #define LIBMOSQUITTO_MINOR 0 -#define LIBMOSQUITTO_REVISION 16 +#define LIBMOSQUITTO_REVISION 17 /* LIBMOSQUITTO_VERSION_NUMBER looks like 1002001 for e.g. version 1.2.1. */ #define LIBMOSQUITTO_VERSION_NUMBER (LIBMOSQUITTO_MAJOR*1000000+LIBMOSQUITTO_MINOR*1000+LIBMOSQUITTO_REVISION) diff --git a/installer/mosquitto.nsi b/installer/mosquitto.nsi index 39659637..284f1641 100644 --- a/installer/mosquitto.nsi +++ b/installer/mosquitto.nsi @@ -9,7 +9,7 @@ !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' Name "Eclipse Mosquitto" -!define VERSION 2.0.16 +!define VERSION 2.0.17 OutFile "mosquitto-${VERSION}-install-windows-x86.exe" InstallDir "$PROGRAMFILES\mosquitto" diff --git a/installer/mosquitto64.nsi b/installer/mosquitto64.nsi index 5b7086bb..fc502241 100644 --- a/installer/mosquitto64.nsi +++ b/installer/mosquitto64.nsi @@ -9,7 +9,7 @@ !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' Name "Eclipse Mosquitto" -!define VERSION 2.0.16 +!define VERSION 2.0.17 OutFile "mosquitto-${VERSION}-install-windows-x64.exe" !include "x64.nsh" diff --git a/set-version.sh b/set-version.sh index 57c3bcb8..ee8d287b 100755 --- a/set-version.sh +++ b/set-version.sh @@ -2,7 +2,7 @@ MAJOR=2 MINOR=0 -REVISION=16 +REVISION=17 sed -i "s/^VERSION=.*/VERSION=${MAJOR}.${MINOR}.${REVISION}/" config.mk diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 177ee2ec..16af47ff 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: mosquitto -version: 2.0.16 +version: 2.0.17 summary: Eclipse Mosquitto MQTT broker description: This is a message broker that supports version 5.0, 3.1.1, and 3.1 of the MQTT protocol. From f762a3fd1ced66b6cd32d8dc137f0523708dac1f Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Tue, 22 Aug 2023 08:43:41 +0100 Subject: [PATCH 78/79] Update docker --- docker/2.0-openssl/Dockerfile | 4 ++-- docker/2.0/Dockerfile | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/2.0-openssl/Dockerfile b/docker/2.0-openssl/Dockerfile index 3f9ea89e..5219bf6f 100644 --- a/docker/2.0-openssl/Dockerfile +++ b/docker/2.0-openssl/Dockerfile @@ -3,8 +3,8 @@ FROM alpine:3.18 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" -ENV VERSION=2.0.16 \ - DOWNLOAD_SHA256=638db9e43e89d243ea98d10d66e76ff376131217780decd01abaffacc66d8035 \ +ENV VERSION=2.0.17 \ + DOWNLOAD_SHA256=3be7a911236567c1a9fbe25baf3e3167004ba4a0c151a448ef1f7fc077dba52f \ GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7 \ LWS_VERSION=4.2.1 \ LWS_SHA256=842da21f73ccba2be59e680de10a8cce7928313048750eb6ad73b6fa50763c51 diff --git a/docker/2.0/Dockerfile b/docker/2.0/Dockerfile index 03ed59d4..8241190d 100644 --- a/docker/2.0/Dockerfile +++ b/docker/2.0/Dockerfile @@ -3,8 +3,8 @@ FROM alpine:3.18 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" -ENV VERSION=2.0.16 \ - DOWNLOAD_SHA256=638db9e43e89d243ea98d10d66e76ff376131217780decd01abaffacc66d8035 \ +ENV VERSION=2.0.17 \ + DOWNLOAD_SHA256=3be7a911236567c1a9fbe25baf3e3167004ba4a0c151a448ef1f7fc077dba52f \ GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7 \ LWS_VERSION=4.2.1 \ LWS_SHA256=842da21f73ccba2be59e680de10a8cce7928313048750eb6ad73b6fa50763c51 From 1ed275b1d4f49cdeca45bbcaa605e75fafa1b140 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Thu, 24 Aug 2023 01:16:51 +0100 Subject: [PATCH 79/79] Version 2.0.17 and below post --- www/posts/2023/08/version-2-0-16-released.md | 84 ++++++++++++++++++++ www/posts/2023/08/version-2-0-17-released.md | 24 ++++++ 2 files changed, 108 insertions(+) create mode 100644 www/posts/2023/08/version-2-0-16-released.md create mode 100644 www/posts/2023/08/version-2-0-17-released.md diff --git a/www/posts/2023/08/version-2-0-16-released.md b/www/posts/2023/08/version-2-0-16-released.md new file mode 100644 index 00000000..4cf08a25 --- /dev/null +++ b/www/posts/2023/08/version-2-0-16-released.md @@ -0,0 +1,84 @@ + + +Version 2.0.16 of Mosquitto has been released. This is a security +and bugfix release. + +# Security +- [CVE-2023-28366]: Fix memory leak in broker when clients send multiple QoS 2 + messages with the same message ID, but then never respond to the PUBREC + commands. +- [CVE-2023-0809]: Fix excessive memory being allocated based on malicious + initial packets that are not CONNECT packets. +- [CVE-2023-3592]: Fix memory leak when clients send v5 CONNECT packets with a + will message that contains invalid property types. +- Broker will now reject Will messages that attempt to publish to $CONTROL/. +- Broker now validates usernames provided in a TLS certificate or TLS-PSK + identity are valid UTF-8. +- Fix potential crash when loading invalid persistence file. +- Library will no longer allow single level wildcard certificates, e.g. *.com + +# Broker +- Fix $SYS messages being expired after 60 seconds and hence unchanged values + disappearing. +- Fix some retained topic memory not being cleared immediately after used. +- Fix error handling related to the `bind_interface` option. +- Fix std* files not being redirected when daemonising, when built with + assertions removed. Closes [#2708]. +- Fix default settings incorrectly allowing TLS v1.1. Closes [#2722]. +- Use line buffered mode for stdout. Closes #2354. Closes [#2749]. +- Fix bridges with non-matching cleansession/local_cleansession being expired + on start after restoring from persistence. Closes [#2634]. +- Fix connections being limited to 2048 on Windows. The limit is now 8192, + where supported. Closes [#2732]. +- Broker will log warnings if sensitive files are world readable/writable, or + if the owner/group is not the same as the user/group the broker is running + as. In future versions the broker will refuse to open these files. +- mosquitto_memcmp_const is now more constant time. +- Only register with DLT if DLT logging is enabled. +- Fix any possible case where a json string might be incorrectly loaded. This + could have caused a crash if a textname or textdescription field of a role was + not a string, when loading the dynsec config from file only. +- Dynsec plugin will not allow duplicate clients/groups/roles when loading + config from file, which matches the behaviour for when creating them. +- Fix heap overflow when reading corrupt config with "log_dest file". + +# Client library +- Use CLOCK_BOOTTIME when available, to keep track of time. This solves the + problem of the client OS sleeping and the client hence not being able to + calculate the actual time for keepalive purposes. Closes [#2760]. +- Fix default settings incorrectly allowing TLS v1.1. Closes [#2722]. +- Fix high CPU use on slow TLS connect. Closes [#2794]. + +# Clients +- Fix incorrect topic-alias property value in mosquitto_sub json output. +- Fix confusing message on TLS certificate verification. Closes [#2746]. + +# Apps +- mosquitto_passwd uses mkstemp() for backup files. +- `mosquitto_ctrl dynsec init` will refuse to overwrite an existing file, + without a race-condition. + +[CVE-2023-0809]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-0809 +[CVE-2023-28366]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-27366 +[CVE-2023-3592]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-3592 +[#2354]: https://github.com/eclipse/mosquitto/issues/2354 +[#2634]: https://github.com/eclipse/mosquitto/issues/2634 +[#2708]: https://github.com/eclipse/mosquitto/issues/2708 +[#2722]: https://github.com/eclipse/mosquitto/issues/2722 +[#2722]: https://github.com/eclipse/mosquitto/issues/2722 +[#2732]: https://github.com/eclipse/mosquitto/issues/2732 +[#2746]: https://github.com/eclipse/mosquitto/issues/2746 +[#2749]: https://github.com/eclipse/mosquitto/issues/2749 +[#2760]: https://github.com/eclipse/mosquitto/issues/2760 +[#2794]: https://github.com/eclipse/mosquitto/issues/2794 +[#1488]: https://github.com/eclipse/mosquitto/issues/1488 + diff --git a/www/posts/2023/08/version-2-0-17-released.md b/www/posts/2023/08/version-2-0-17-released.md new file mode 100644 index 00000000..4dd16a48 --- /dev/null +++ b/www/posts/2023/08/version-2-0-17-released.md @@ -0,0 +1,24 @@ + + +Version 2.0.16 of Mosquitto has been released. This is a bugfix release. + +Broker: +- Fix `max_queued_messages 0` stopping clients from receiving messages. + Closes [#2879]. +- Fix `max_inflight_messages` not being set correctly. Closes [#2876]. + +Apps: +- Fix `mosquitto_passwd -U` backup file creation. Closes [#2873]. + +[#2873]: https://github.com/eclipse/mosquitto/issues/2873 +[#2876]: https://github.com/eclipse/mosquitto/issues/2876 +[#2879]: https://github.com/eclipse/mosquitto/issues/2879