diff --git a/CMakeLists.txt b/CMakeLists.txt index d9a86362..161696da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ project(mosquitto) cmake_minimum_required(VERSION 2.8) # Only for version 3 and up. cmake_policy(SET CMP0042 NEW) -set (VERSION 1.5.3) +set (VERSION 1.5.4) add_definitions (-DCMAKE -DVERSION=\"${VERSION}\") @@ -73,6 +73,30 @@ endif (${WITH_SOCKS} STREQUAL ON) option(WITH_SRV "Include SRV lookup support?" OFF) +option(WITH_THREADING "Include client library threading support?" ON) +if (${WITH_THREADING} STREQUAL ON) + add_definitions("-DWITH_THREADING") + if (WIN32) + if (CMAKE_CL_64) + set (PTHREAD_LIBRARIES C:\\pthreads\\Pre-built.2\\lib\\x64\\pthreadVC2.lib) + else (CMAKE_CL_64) + set (PTHREAD_LIBRARIES C:\\pthreads\\Pre-built.2\\lib\\x86\\pthreadVC2.lib) + endif (CMAKE_CL_64) + set (PTHREAD_INCLUDE_DIR C:\\pthreads\\Pre-built.2\\include) + else (WIN32) + find_library(LIBPTHREAD pthread) + if (LIBPTHREAD) + set (PTHREAD_LIBRARIES pthread) + else (LIBPTHREAD) + set (PTHREAD_LIBRARIES "") + endif() + set (PTHREAD_INCLUDE_DIR "") + endif (WIN32) +else (${WITH_THREADING} STREQUAL ON) + set (PTHREAD_LIBRARIES "") + set (PTHREAD_INCLUDE_DIR "") +endif (${WITH_THREADING} STREQUAL ON) + option(DOCUMENTATION "Build documentation?" ON) # ======================================== diff --git a/ChangeLog.txt b/ChangeLog.txt index 18c5514d..cc35d767 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -1,3 +1,39 @@ +1.5.4 - 20181108 +================ + +Security: +- When using a TLS enabled websockets listener with "require_certificate" + enabled, the mosquitto broker does not correctly verify client certificates. + This is now fixed. All other security measures operate as expected, and in + particular non-websockets listeners are not affected by this. Closes #996. + +Broker: +- Process all pending messages even when a client has disconnected. This means + a client that send a PUBLISH then DISCONNECT quickly, then disconnects will + have its DISCONNECT message processed properly and so no Will will be sent. + Closes #7. +- $SYS/broker/clients/disconnected should never be negative. Closes #287. +- Give better error message if a client sends a password without a username. + Closes #1015. +- Fix bridge not honoring restart_timeout. Closes #1019. +- Don't disconnect a client if an auth plugin denies access to SUBSCRIBE. + Closes #1016. + +Library: +- Fix memory leak that occurred if mosquitto_reconnect() was used when TLS + errors were present. Closes #592. +- Fix TLS connections when using an external event loop with + mosquitto_loop_read() and mosquitto_write(). Closes #990. + +Build: +- Fix clients not being compiled with threading support when using CMake. + Closes #983. +- Header fixes for FreeBSD. Closes #977. +- Use _GNU_SOURCE to fix build errors in websockets and getaddrinfo usage. + Closes #862 and #933. +- Fix builds on QNX 7.0.0. Closes #1018. + + 1.5.3 - 20180925 ================ @@ -16,6 +52,8 @@ Broker: removed. Closes #645. - Fix Windows version not starting if include_dir did not contain any files. Closes #566. +- When an authentication plugin denied access to a SUBSCRIBE, the client would + be disconnected incorrectly. This has been fixed. Closes #1016. Build: - Various fixes to ease building. diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index a6b2c018..660bf1ac 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,5 +1,5 @@ include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/lib - ${STDBOOL_H_PATH} ${STDINT_H_PATH}) + ${STDBOOL_H_PATH} ${STDINT_H_PATH} ${PTHREAD_INCLUDE_DIR}) link_directories(${mosquitto_BINARY_DIR}/lib) set(shared_src client_shared.c client_shared.h) @@ -11,8 +11,14 @@ endif (${WITH_SRV} STREQUAL ON) add_executable(mosquitto_pub pub_client.c ${shared_src}) add_executable(mosquitto_sub sub_client.c sub_client_output.c ${shared_src}) + target_link_libraries(mosquitto_pub libmosquitto) target_link_libraries(mosquitto_sub libmosquitto) +if (QNX) + target_link_libraries(mosquitto_pub socket) + target_link_libraries(mosquitto_sub socket) +endif() + install(TARGETS mosquitto_pub RUNTIME DESTINATION "${BINDIR}" LIBRARY DESTINATION "${LIBDIR}") install(TARGETS mosquitto_sub RUNTIME DESTINATION "${BINDIR}" LIBRARY DESTINATION "${LIBDIR}") diff --git a/client/Makefile b/client/Makefile index 844bc6e0..824d703c 100644 --- a/client/Makefile +++ b/client/Makefile @@ -2,7 +2,19 @@ include ../config.mk .PHONY: all install uninstall reallyclean clean static static_pub static_sub -all : mosquitto_pub mosquitto_sub +ifeq ($(WITH_SHARED_LIBRARIES),yes) +SHARED_DEP:=../lib/libmosquitto.so.${SOVERSION} +endif + +ifeq ($(WITH_SHARED_LIBRARIES),yes) +ALL_DEPS:= mosquitto_pub mosquitto_sub +else +ifeq ($(WITH_STATIC_LIBRARIES),yes) +ALL_DEPS:= static_pub static_sub +endif +endif + +all : ${ALL_DEPS} static : static_pub static_sub # This makes mosquitto_pub/sub versions that are statically linked with @@ -20,13 +32,13 @@ mosquitto_pub : pub_client.o client_shared.o mosquitto_sub : sub_client.o sub_client_output.o client_shared.o ${CROSS_COMPILE}${CC} $^ -o $@ ${CLIENT_LDFLAGS} -pub_client.o : pub_client.c ../lib/libmosquitto.so.${SOVERSION} +pub_client.o : pub_client.c ${SHARED_DEP} ${CROSS_COMPILE}${CC} -c $< -o $@ ${CLIENT_CFLAGS} -sub_client.o : sub_client.c ../lib/libmosquitto.so.${SOVERSION} +sub_client.o : sub_client.c ${SHARED_DEP} ${CROSS_COMPILE}${CC} -c $< -o $@ ${CLIENT_CFLAGS} -sub_client_output.o : sub_client_output.c ../lib/libmosquitto.so.${SOVERSION} +sub_client_output.o : sub_client_output.c ${SHARED_DEP} ${CROSS_COMPILE}${CC} -c $< -o $@ ${CLIENT_CFLAGS} client_shared.o : client_shared.c client_shared.h diff --git a/client/client_shared.c b/client/client_shared.c index d5d37256..2788b7ce 100644 --- a/client/client_shared.c +++ b/client/client_shared.c @@ -974,7 +974,11 @@ int client_id_generate(struct mosq_config *cfg, const char *id_base) int client_connect(struct mosquitto *mosq, struct mosq_config *cfg) { +#ifndef WIN32 + char *err; +#else char err[1024]; +#endif int rc; int port; @@ -1008,7 +1012,7 @@ int client_connect(struct mosquitto *mosq, struct mosq_config *cfg) if(!cfg->quiet){ if(rc == MOSQ_ERR_ERRNO){ #ifndef WIN32 - strerror_r(errno, err, 1024); + err = strerror(errno); #else FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errno, 0, (LPTSTR)&err, 1024, NULL); #endif diff --git a/config.h b/config.h index 5e76e899..97ac6be9 100644 --- a/config.h +++ b/config.h @@ -15,6 +15,7 @@ # define _POSIX_C_SOURCE 200809L #endif +#define _GNU_SOURCE /* ============================================================ * Compatibility defines diff --git a/config.mk b/config.mk index 46ffb6db..d591cd49 100644 --- a/config.mk +++ b/config.mk @@ -86,6 +86,9 @@ WITH_STRIP:=no # Build static libraries WITH_STATIC_LIBRARIES:=no +# Build shared libraries +WITH_SHARED_LIBRARIES:=yes + # Build with async dns lookup support for bridges (temporary). Requires glibc. #WITH_ADNS:=yes @@ -102,7 +105,7 @@ WITH_BUNDLED_DEPS:=yes # Also bump lib/mosquitto.h, CMakeLists.txt, # installer/mosquitto.nsi, installer/mosquitto64.nsi -VERSION=1.5.3 +VERSION=1.5.4 # Client library SO version. Bump if incompatible API/ABI changes are made. SOVERSION=1 @@ -146,7 +149,10 @@ ifeq ($(UNAME),Linux) LIB_LIBS:=$(LIB_LIBS) -lrt endif -CLIENT_LDFLAGS:=$(LDFLAGS) -L../lib ../lib/libmosquitto.so.${SOVERSION} +CLIENT_LDFLAGS:=$(LDFLAGS) -L../lib +ifeq ($(WITH_SHARED_LIBRARIES),yes) + CLIENT_LDFLAGS:=${CLIENT_LDFLAGS} ../lib/libmosquitto.so.${SOVERSION} +endif ifeq ($(UNAME),SunOS) ifeq ($(CC),cc) diff --git a/installer/mosquitto.nsi b/installer/mosquitto.nsi index b97f6f3a..34143f85 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 1.5.3 +!define VERSION 1.5.4 OutFile "mosquitto-${VERSION}-install-windows-x86.exe" InstallDir "$PROGRAMFILES\mosquitto" diff --git a/installer/mosquitto64.nsi b/installer/mosquitto64.nsi index 59acea1a..097f4521 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 1.5.3 +!define VERSION 1.5.4 OutFile "mosquitto-${VERSION}-install-windows-x64.exe" !include "x64.nsh" diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index d537e776..c92571aa 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -2,30 +2,6 @@ option(WITH_STATIC_LIBRARIES "Build static versions of the libmosquitto/pp libra option(WITH_PIC "Build the static library with PIC (Position Independent Code) enabled archives?" OFF) add_subdirectory(cpp) -option(WITH_THREADING "Include client library threading support?" ON) -if (${WITH_THREADING} STREQUAL ON) - add_definitions("-DWITH_THREADING") - if (WIN32) - if (CMAKE_CL_64) - set (PTHREAD_LIBRARIES C:\\pthreads\\Pre-built.2\\lib\\x64\\pthreadVC2.lib) - else (CMAKE_CL_64) - set (PTHREAD_LIBRARIES C:\\pthreads\\Pre-built.2\\lib\\x86\\pthreadVC2.lib) - endif (CMAKE_CL_64) - set (PTHREAD_INCLUDE_DIR C:\\pthreads\\Pre-built.2\\include) - else (WIN32) - find_library(LIBPTHREAD pthread) - if (LIBPTHREAD) - set (PTHREAD_LIBRARIES pthread) - else (LIBPTHREAD) - set (PTHREAD_LIBRARIES "") - endif() - set (PTHREAD_INCLUDE_DIR "") - endif (WIN32) -else (${WITH_THREADING} STREQUAL ON) - set (PTHREAD_LIBRARIES "") - set (PTHREAD_INCLUDE_DIR "") -endif (${WITH_THREADING} STREQUAL ON) - include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/lib ${STDBOOL_H_PATH} ${STDINT_H_PATH} ${OPENSSL_INCLUDE_DIR} ${PTHREAD_INCLUDE_DIR}) diff --git a/lib/Makefile b/lib/Makefile index b768c81b..4c9cac43 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -38,19 +38,27 @@ MOSQ_OBJS=mosquitto.o \ util_mosq.o \ will_mosq.o -ALL_DEPS=libmosquitto.so.${SOVERSION} +ALL_DEPS:= + +ifeq ($(WITH_SHARED_LIBRARIES),yes) + ALL_DEPS+=libmosquitto.so.${SOVERSION} +endif ifeq ($(WITH_STATIC_LIBRARIES),yes) ALL_DEPS+=libmosquitto.a endif all : ${ALL_DEPS} +ifeq ($(WITH_SHARED_LIBRARIES),yes) $(MAKE) -C cpp +endif install : all $(INSTALL) -d "${DESTDIR}${libdir}/" +ifeq ($(WITH_SHARED_LIBRARIES),yes) $(INSTALL) ${STRIP_OPTS} libmosquitto.so.${SOVERSION} "${DESTDIR}${libdir}/libmosquitto.so.${SOVERSION}" ln -sf libmosquitto.so.${SOVERSION} "${DESTDIR}${libdir}/libmosquitto.so" +endif ifeq ($(WITH_STATIC_LIBRARIES),yes) $(INSTALL) ${STRIP_OPTS} libmosquitto.a "${DESTDIR}${libdir}/libmosquitto.a" endif @@ -59,7 +67,9 @@ endif $(INSTALL) -d "${DESTDIR}${libdir}/pkgconfig" $(INSTALL) -m644 ../libmosquitto.pc.in "${DESTDIR}${libdir}/pkgconfig/libmosquitto.pc" sed -i -e "s#@CMAKE_INSTALL_PREFIX@#${prefix}#" -e "s#@VERSION@#${VERSION}#" "${DESTDIR}${libdir}/pkgconfig/libmosquitto.pc" +ifeq ($(WITH_SHARED_LIBRARIES),yes) $(MAKE) -C cpp install +endif uninstall : -rm -f "${DESTDIR}${libdir}/libmosquitto.so.${SOVERSION}" diff --git a/lib/loop.c b/lib/loop.c index 0725d227..23e60825 100644 --- a/lib/loop.c +++ b/lib/loop.c @@ -147,20 +147,12 @@ int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets) }else{ if(mosq->sock != INVALID_SOCKET){ if(FD_ISSET(mosq->sock, &readfds)){ -#ifdef WITH_TLS - if(mosq->want_connect){ - rc = net__socket_connect_tls(mosq); - if(rc) return rc; - }else -#endif - { - do{ - rc = mosquitto_loop_read(mosq, max_packets); - if(rc || mosq->sock == INVALID_SOCKET){ - return rc; - } - }while(SSL_DATA_PENDING(mosq)); - } + do{ + rc = mosquitto_loop_read(mosq, max_packets); + if(rc || mosq->sock == INVALID_SOCKET){ + return rc; + } + }while(SSL_DATA_PENDING(mosq)); } if(mosq->sockpairR != INVALID_SOCKET && FD_ISSET(mosq->sockpairR, &readfds)){ #ifndef WIN32 @@ -354,6 +346,12 @@ int mosquitto_loop_read(struct mosquitto *mosq, int max_packets) int i; if(max_packets < 1) return MOSQ_ERR_INVAL; +#ifdef WITH_TLS + if(mosq->want_connect){ + return net__socket_connect_tls(mosq); + } +#endif + pthread_mutex_lock(&mosq->out_message_mutex); max_packets = mosq->out_queue_len; pthread_mutex_unlock(&mosq->out_message_mutex); diff --git a/lib/mosquitto.h b/lib/mosquitto.h index 3d15666d..b3600b2c 100644 --- a/lib/mosquitto.h +++ b/lib/mosquitto.h @@ -47,7 +47,7 @@ extern "C" { #define LIBMOSQUITTO_MAJOR 1 #define LIBMOSQUITTO_MINOR 5 -#define LIBMOSQUITTO_REVISION 3 +#define LIBMOSQUITTO_REVISION 4 /* 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/lib/net_mosq.c b/lib/net_mosq.c index 5494abae..4efda3d2 100644 --- a/lib/net_mosq.c +++ b/lib/net_mosq.c @@ -596,6 +596,9 @@ int net__socket_connect_step3(struct mosquitto *mosq, const char *host, uint16_t if(rc) return rc; if(mosq->ssl_ctx){ + if(mosq->ssl){ + SSL_free(mosq->ssl); + } mosq->ssl = SSL_new(mosq->ssl_ctx); if(!mosq->ssl){ COMPAT_CLOSE(mosq->sock); diff --git a/lib/socks_mosq.c b/lib/socks_mosq.c index f8f006a4..e7d597f7 100644 --- a/lib/socks_mosq.c +++ b/lib/socks_mosq.c @@ -21,9 +21,16 @@ Contributors: #include #ifdef WIN32 # include +#elif __QNX__ +# include +# include #else # include #endif +#ifdef __FreeBSD__ +# include +# include +#endif #include "mosquitto_internal.h" #include "memory_mosq.h" diff --git a/set-version.sh b/set-version.sh index 522dfa1d..54958189 100755 --- a/set-version.sh +++ b/set-version.sh @@ -2,7 +2,7 @@ MAJOR=1 MINOR=5 -REVISION=3 +REVISION=4 sed -i "s/^VERSION=.*/VERSION=${MAJOR}.${MINOR}.${REVISION}/" config.mk diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index f3a6d1cf..fa1127ed 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: mosquitto -version: 1.5.3-1 +version: 1.5.4 summary: Eclipse Mosquitto MQTT broker description: This is a message broker that supports version 3.1 and 3.1.1 of the MQTT protocol. diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dab82a48..187d0aac 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -126,17 +126,18 @@ if (HAVE_GETADDRINFO_A) endif (HAVE_GETADDRINFO_A) - if (UNIX) if (APPLE) set (MOSQ_LIBS ${MOSQ_LIBS} dl m) - else (APPLE) - set (MOSQ_LIBS ${MOSQ_LIBS} dl m) - find_library(LIBRT rt) - if (LIBRT) - set (MOSQ_LIBS ${MOSQ_LIBS} rt) - endif (LIBRT) - endif (APPLE) + elseif(QNX) + set(MOSQ_LIBS ${MOSQ_LIBS} m socket) + else(APPLE) + set (MOSQ_LIBS ${MOSQ_LIBS} dl m) + find_library(LIBRT rt) + if (LIBRT) + set (MOSQ_LIBS ${MOSQ_LIBS} rt) + endif (LIBRT) + endif (APPLE) endif (UNIX) if (WIN32) diff --git a/src/conf.c b/src/conf.c index 367868e5..0b1d79cf 100644 --- a/src/conf.c +++ b/src/conf.c @@ -2126,6 +2126,7 @@ static int conf__parse_bool(char **token, const char *name, bool *value, char *s *value = true; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid %s value (%s).", name, *token); + return MOSQ_ERR_INVAL; } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty %s value in configuration.", name); diff --git a/src/conf_includedir.c b/src/conf_includedir.c index 43da9a16..783a4048 100644 --- a/src/conf_includedir.c +++ b/src/conf_includedir.c @@ -36,7 +36,7 @@ Contributors: # include #endif -#if !defined(WIN32) && !defined(__CYGWIN__) +#if !defined(WIN32) && !defined(__CYGWIN__) && !defined(__QNX__) # include #endif diff --git a/src/handle_connect.c b/src/handle_connect.c index 87964456..fd60addf 100644 --- a/src/handle_connect.c +++ b/src/handle_connect.c @@ -395,6 +395,7 @@ int handle__connect(struct mosquitto_db *db, struct mosquitto *context) if(context->protocol == mosq_p_mqtt311){ if(password_flag){ /* username_flag == 0 && password_flag == 1 is forbidden */ + log__printf(NULL, MOSQ_LOG_ERR, "Protocol error from %s: password without username, closing connection.", client_id); rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } diff --git a/src/handle_subscribe.c b/src/handle_subscribe.c index 3b2e2591..8f594351 100644 --- a/src/handle_subscribe.c +++ b/src/handle_subscribe.c @@ -112,8 +112,8 @@ int handle__subscribe(struct mosquitto_db *db, struct mosquitto *context) log__printf(NULL, MOSQ_LOG_DEBUG, "\t%s (QoS %d)", sub, qos); if(context->protocol == mosq_p_mqtt311){ - rc = mosquitto_acl_check(db, context, sub, 0, NULL, qos, false, MOSQ_ACL_SUBSCRIBE); - switch(rc){ + rc2 = mosquitto_acl_check(db, context, sub, 0, NULL, qos, false, MOSQ_ACL_SUBSCRIBE); + switch(rc2){ case MOSQ_ERR_SUCCESS: break; case MOSQ_ERR_ACL_DENIED: @@ -121,7 +121,7 @@ int handle__subscribe(struct mosquitto_db *db, struct mosquitto *context) break; default: mosquitto__free(sub); - return rc; + return rc2; } } diff --git a/src/loop.c b/src/loop.c index 97147209..c049c8ef 100644 --- a/src/loop.c +++ b/src/loop.c @@ -422,8 +422,8 @@ int mosquitto_main_loop(struct mosquitto_db *db, mosq_sock_t *listensock, int li #else { rc = bridge__connect(db, context); + context->bridge->restart_t = 0; if(rc == MOSQ_ERR_SUCCESS){ - context->bridge->restart_t = 0; if(context->bridge->round_robin == false && context->bridge->cur_address != 0){ context->bridge->primary_retry = now + 5; } @@ -638,9 +638,11 @@ void do_disconnect(struct mosquitto_db *db, struct mosquitto *context) context->sock = INVALID_SOCKET; context->pollfd_index = -1; } - HASH_DELETE(hh_id, db->contexts_by_id, context); - context->old_id = context->id; - context->id = NULL; + if(context->id){ + HASH_DELETE(hh_id, db->contexts_by_id, context); + context->old_id = context->id; + context->id = NULL; + } }else #endif { @@ -811,14 +813,15 @@ static void loop_handle_reads_writes(struct mosquitto_db *db, struct pollfd *pol continue; } }while(SSL_DATA_PENDING(context)); - } + }else{ #ifdef WITH_EPOLL - if(events & (EPOLLERR | EPOLLHUP)){ + if(events & (EPOLLERR | EPOLLHUP)){ #else - if(context->pollfd_index >= 0 && pollfds[context->pollfd_index].revents & (POLLERR | POLLNVAL | POLLHUP)){ + if(context->pollfd_index >= 0 && pollfds[context->pollfd_index].revents & (POLLERR | POLLNVAL | POLLHUP)){ #endif - do_disconnect(db, context); - continue; + do_disconnect(db, context); + continue; + } } } } diff --git a/src/mosquitto.c b/src/mosquitto.c index eb90260c..768d5c10 100644 --- a/src/mosquitto.c +++ b/src/mosquitto.c @@ -91,7 +91,7 @@ int drop_privileges(struct mosquitto__config *config, bool temporary) { #if !defined(__CYGWIN__) && !defined(WIN32) struct passwd *pwd; - char err[256]; + char *err; int rc; const char *snap = getenv("SNAP_NAME"); @@ -108,7 +108,7 @@ int drop_privileges(struct mosquitto__config *config, bool temporary) return 1; } if(initgroups(config->user, pwd->pw_gid) == -1){ - strerror_r(errno, err, 256); + err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error setting groups whilst dropping privileges: %s.", err); return 1; } @@ -118,7 +118,7 @@ int drop_privileges(struct mosquitto__config *config, bool temporary) rc = setgid(pwd->pw_gid); } if(rc == -1){ - strerror_r(errno, err, 256); + err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error setting gid whilst dropping privileges: %s.", err); return 1; } @@ -128,7 +128,7 @@ int drop_privileges(struct mosquitto__config *config, bool temporary) rc = setuid(pwd->pw_uid); } if(rc == -1){ - strerror_r(errno, err, 256); + err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error setting uid whilst dropping privileges: %s.", err); return 1; } @@ -144,19 +144,19 @@ int drop_privileges(struct mosquitto__config *config, bool temporary) int restore_privileges(void) { #if !defined(__CYGWIN__) && !defined(WIN32) - char err[256]; + char *err; int rc; if(getuid() == 0){ rc = setegid(0); if(rc == -1){ - strerror_r(errno, err, 256); + err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error setting gid whilst restoring privileges: %s.", err); return 1; } rc = seteuid(0); if(rc == -1){ - strerror_r(errno, err, 256); + err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error setting uid whilst restoring privileges: %s.", err); return 1; } @@ -169,12 +169,12 @@ int restore_privileges(void) void mosquitto__daemonise(void) { #ifndef WIN32 - char err[256]; + char *err; pid_t pid; pid = fork(); if(pid < 0){ - strerror_r(errno, err, 256); + err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error in fork: %s", err); exit(1); } @@ -182,7 +182,7 @@ void mosquitto__daemonise(void) exit(0); } if(setsid() < 0){ - strerror_r(errno, err, 256); + err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error in setsid: %s", err); exit(1); } diff --git a/src/net.c b/src/net.c index 780a5f0b..08f2b824 100644 --- a/src/net.c +++ b/src/net.c @@ -81,18 +81,16 @@ void net__broker_cleanup(void) static void net__print_error(int log, const char *format_str) { -#ifdef WIN32 char *buf; +#ifdef WIN32 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, WSAGetLastError(), LANG_NEUTRAL, &buf, 0, NULL); log__printf(NULL, log, format_str, buf); LocalFree(buf); #else - char buf[256]; - - strerror_r(errno, buf, 256); + buf = strerror(errno); log__printf(NULL, log, format_str, buf); #endif } diff --git a/src/persist.c b/src/persist.c index 7a93f984..b83327c3 100644 --- a/src/persist.c +++ b/src/persist.c @@ -356,7 +356,7 @@ int persist__backup(struct mosquitto_db *db, bool shutdown) uint32_t i32temp; uint16_t i16temp; uint8_t i8temp; - char err[256]; + char *err; char *outfile = NULL; int len; @@ -477,7 +477,7 @@ int persist__backup(struct mosquitto_db *db, bool shutdown) return rc; error: mosquitto__free(outfile); - strerror_r(errno, err, 256); + err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err); if(db_fptr) fclose(db_fptr); return 1; @@ -596,7 +596,7 @@ static int persist__client_msg_chunk_restore(struct mosquitto_db *db, FILE *db_f uint8_t qos, retain, direction, state, dup; char *client_id = NULL; int rc; - char err[256]; + char *err; read_e(db_fptr, &i16temp, sizeof(uint16_t)); slen = ntohs(i16temp); @@ -631,7 +631,7 @@ static int persist__client_msg_chunk_restore(struct mosquitto_db *db, FILE *db_f return rc; error: - strerror_r(errno, err, 256); + err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err); fclose(db_fptr); mosquitto__free(client_id); @@ -650,7 +650,7 @@ static int persist__msg_store_chunk_restore(struct mosquitto_db *db, FILE *db_fp int rc = 0; struct mosquitto_msg_store *stored = NULL; struct mosquitto_msg_store_load *load; - char err[256]; + char *err; payload.ptr = NULL; @@ -734,7 +734,7 @@ static int persist__msg_store_chunk_restore(struct mosquitto_db *db, FILE *db_fp return rc; } error: - strerror_r(errno, err, 256); + err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err); fclose(db_fptr); mosquitto__free(source_id); @@ -747,10 +747,10 @@ static int persist__retain_chunk_restore(struct mosquitto_db *db, FILE *db_fptr) { dbid_t i64temp, store_id; struct mosquitto_msg_store_load *load; - char err[256]; + char *err; if(fread(&i64temp, sizeof(dbid_t), 1, db_fptr) != 1){ - strerror_r(errno, err, 256); + err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err); fclose(db_fptr); return 1; @@ -773,7 +773,7 @@ static int persist__sub_chunk_restore(struct mosquitto_db *db, FILE *db_fptr) char *client_id; char *topic; int rc = 0; - char err[256]; + char *err; read_e(db_fptr, &i16temp, sizeof(uint16_t)); slen = ntohs(i16temp); @@ -807,7 +807,7 @@ static int persist__sub_chunk_restore(struct mosquitto_db *db, FILE *db_fptr) return rc; error: - strerror_r(errno, err, 256); + err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err); fclose(db_fptr); return 1; @@ -824,7 +824,7 @@ int persist__restore(struct mosquitto_db *db) uint16_t i16temp, chunk; uint8_t i8temp; ssize_t rlen; - char err[256]; + char *err; struct mosquitto_msg_store_load *load, *load_tmp; assert(db); @@ -919,7 +919,7 @@ int persist__restore(struct mosquitto_db *db) } return rc; error: - strerror_r(errno, err, 256); + err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err); if(fptr) fclose(fptr); return 1; diff --git a/src/sys_tree.c b/src/sys_tree.c index ebece86b..65800fcf 100644 --- a/src/sys_tree.c +++ b/src/sys_tree.c @@ -60,8 +60,8 @@ static void sys_tree__update_clients(struct mosquitto_db *db, char *buf) static unsigned int client_count = -1; static int clients_expired = -1; static unsigned int client_max = 0; - static unsigned int disconnected_count = -1; - static unsigned int connected_count = -1; + static int disconnected_count = -1; + static int connected_count = -1; unsigned int count_total, count_by_sock; @@ -82,6 +82,13 @@ static void sys_tree__update_clients(struct mosquitto_db *db, char *buf) if(disconnected_count != count_total-count_by_sock){ disconnected_count = count_total-count_by_sock; + if(disconnected_count < 0){ + /* If a client has connected but not sent a CONNECT at this point, + * then it is possible that count_by_sock will be bigger than + * count_total, causing a negative number. This situation should + * not last for long, so just cap at zero and ignore. */ + disconnected_count = 0; + } snprintf(buf, BUFLEN, "%d", disconnected_count); db__messages_easy_queue(db, NULL, "$SYS/broker/clients/inactive", SYS_TREE_QOS, strlen(buf), buf, 1); db__messages_easy_queue(db, NULL, "$SYS/broker/clients/disconnected", SYS_TREE_QOS, strlen(buf), buf, 1); diff --git a/src/websockets.c b/src/websockets.c index 1ab02a9b..bf2804b8 100644 --- a/src/websockets.c +++ b/src/websockets.c @@ -665,6 +665,14 @@ static int callback_http(struct libwebsocket_context *context, } break; +#ifdef WITH_TLS + case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION: + if(!len || (SSL_get_verify_result((SSL*)in) != X509_V_OK)){ + return 1; + } + break; +#endif + default: return 0; } diff --git a/test/broker/09-plugin-auth-acl-sub-denied.py b/test/broker/09-plugin-auth-acl-sub-denied.py new file mode 100755 index 00000000..83726653 --- /dev/null +++ b/test/broker/09-plugin-auth-acl-sub-denied.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +# Test topic subscription. All SUBSCRIBE requests are denied. Check this +# produces the correct response, and check the client isn't disconnected (ref: +# issue #1016). + +import inspect, os, sys +# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder +cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) +if cmd_subfolder not in sys.path: + sys.path.insert(0, cmd_subfolder) + +import mosq_test + +def write_config(filename, port): + with open(filename, 'w') as f: + f.write("port %d\n" % (port)) + f.write("auth_plugin c/auth_plugin_acl_sub_denied.so\n") + f.write("allow_anonymous false\n") + +port = mosq_test.get_port() +conf_file = os.path.basename(__file__).replace('.py', '.conf') +write_config(conf_file, port) + +rc = 1 +keepalive = 10 +connect_packet = mosq_test.gen_connect("sub-denied-test", keepalive=keepalive, username="denied") +connack_packet = mosq_test.gen_connack(rc=0) + +mid = 53 +subscribe_packet = mosq_test.gen_subscribe(mid, "qos0/test", 0) +suback_packet = mosq_test.gen_suback(mid, 128) + +mid_pub = 54 +publish_packet = mosq_test.gen_publish("topic", qos=1, payload="test", mid=mid_pub) +puback_packet = mosq_test.gen_puback(mid_pub) + +broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) + +try: + sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) + mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") + + mosq_test.do_send_receive(sock, publish_packet, puback_packet, "puback") + + rc = 0 + + sock.close() +finally: + os.remove(conf_file) + broker.terminate() + broker.wait() + (stdo, stde) = broker.communicate() + if rc: + print(stde) + + +exit(rc) diff --git a/test/broker/Makefile b/test/broker/Makefile index 0b4fee08..c70f1f7d 100644 --- a/test/broker/Makefile +++ b/test/broker/Makefile @@ -116,6 +116,7 @@ endif ./09-plugin-auth-unpwd-success.py ./09-plugin-auth-unpwd-fail.py ./09-plugin-auth-acl-sub.py + ./09-plugin-auth-acl-sub-denied.py ./09-plugin-auth-v2-unpwd-success.py ./09-plugin-auth-v2-unpwd-fail.py ./09-plugin-auth-defer-unpwd-success.py diff --git a/test/broker/c/Makefile b/test/broker/c/Makefile index f60f9d5f..0ce17768 100644 --- a/test/broker/c/Makefile +++ b/test/broker/c/Makefile @@ -2,7 +2,17 @@ CFLAGS=-I../../../lib -I../../../src -Wall -Werror -all : auth_plugin.so auth_plugin_pwd.so auth_plugin_acl.so auth_plugin_v2.so auth_plugin_msg_params.so auth_plugin_context_params.so 08 +PLUGINS= \ + auth_plugin.so \ + auth_plugin_pwd.so \ + auth_plugin_acl.so \ + auth_plugin_v2.so \ + auth_plugin_msg_params.so \ + auth_plugin_context_params.so \ + auth_plugin_acl_sub_denied.so + + +all : ${PLUGINS} 08 08 : 08-tls-psk-pub.test 08-tls-psk-bridge.test @@ -24,6 +34,9 @@ auth_plugin_context_params.so : auth_plugin_context_params.c auth_plugin_msg_params.so : auth_plugin_msg_params.c $(CC) ${CFLAGS} -fPIC -shared $^ -o $@ +auth_plugin_acl_sub_denied.so : auth_plugin_acl_sub_denied.c + $(CC) ${CFLAGS} -fPIC -shared $^ -o $@ + 08-tls-psk-pub.test : 08-tls-psk-pub.c $(CC) ${CFLAGS} $^ -o $@ ../../../lib/libmosquitto.so.1 diff --git a/test/broker/c/auth_plugin_acl_sub_denied.c b/test/broker/c/auth_plugin_acl_sub_denied.c new file mode 100644 index 00000000..4c5a26fa --- /dev/null +++ b/test/broker/c/auth_plugin_acl_sub_denied.c @@ -0,0 +1,49 @@ +#include +#include +#include +#include +#include + +int mosquitto_auth_plugin_version(void) +{ + return MOSQ_AUTH_PLUGIN_VERSION; +} + +int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) +{ + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) +{ + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) +{ + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) +{ + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_auth_acl_check(void *user_data, int access, const struct mosquitto *client, const struct mosquitto_acl_msg *msg) +{ + if(access == MOSQ_ACL_SUBSCRIBE){ + return MOSQ_ERR_ACL_DENIED; + }else{ + return MOSQ_ERR_SUCCESS; + } +} + +int mosquitto_auth_unpwd_check(void *user_data, const struct mosquitto *client, const char *username, const char *password) +{ + return MOSQ_ERR_SUCCESS; +} + +int mosquitto_auth_psk_key_get(void *user_data, const struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len) +{ + return MOSQ_ERR_AUTH; +} diff --git a/test/broker/ptest.py b/test/broker/ptest.py index 27b0df6d..f39049e1 100755 --- a/test/broker/ptest.py +++ b/test/broker/ptest.py @@ -88,6 +88,7 @@ tests = [ (1, './09-plugin-auth-unpwd-success.py'), (1, './09-plugin-auth-unpwd-fail.py'), (1, './09-plugin-auth-acl-sub.py'), + (1, './09-plugin-auth-acl-sub-denied.py'), (1, './09-plugin-auth-v2-unpwd-success.py'), (1, './09-plugin-auth-v2-unpwd-fail.py'), (1, './09-plugin-auth-defer-unpwd-success.py'), diff --git a/test/ssl/gen.sh b/test/ssl/gen.sh index 7a49631c..a52159c1 100755 --- a/test/ssl/gen.sh +++ b/test/ssl/gen.sh @@ -31,42 +31,42 @@ openssl req -new -x509 -days 3650 -key test-fake-root-ca.key -out test-fake-root # An intermediate CA, signed by the root CA, used to sign server/client csrs. openssl genrsa -out test-signing-ca.key 1024 openssl req -out test-signing-ca.csr -key test-signing-ca.key -new -config openssl.cnf -subj "${BASESUBJ}/CN=Signing CA/" -openssl ca -config openssl.cnf -name CA_root -extensions v3_ca -out test-signing-ca.crt -infiles test-signing-ca.csr +openssl ca -batch -config openssl.cnf -name CA_root -extensions v3_ca -out test-signing-ca.crt -infiles test-signing-ca.csr # An alternative intermediate CA, signed by the root CA, not used to sign anything. openssl genrsa -out test-alt-ca.key 1024 openssl req -out test-alt-ca.csr -key test-alt-ca.key -new -config openssl.cnf -subj "${BASESUBJ}/CN=Alternative Signing CA/" -openssl ca -config openssl.cnf -name CA_root -extensions v3_ca -out test-alt-ca.crt -infiles test-alt-ca.csr +openssl ca -batch -config openssl.cnf -name CA_root -extensions v3_ca -out test-alt-ca.crt -infiles test-alt-ca.csr # Valid server key and certificate. openssl genrsa -out server.key 1024 openssl req -new -key server.key -out server.csr -config openssl.cnf -subj "${SBASESUBJ}/CN=localhost/" -openssl ca -config openssl.cnf -name CA_signing -out server.crt -infiles server.csr +openssl ca -batch -config openssl.cnf -name CA_signing -out server.crt -infiles server.csr # Expired server certificate, based on the above server key. openssl req -new -days 1 -key server.key -out server-expired.csr -config openssl.cnf -subj "${SBASESUBJ}/CN=localhost/" -openssl ca -config openssl.cnf -name CA_signing -days 1 -startdate 120820000000Z -enddate 120821000000Z -out server-expired.crt -infiles server-expired.csr +openssl ca -batch -config openssl.cnf -name CA_signing -days 1 -startdate 120820000000Z -enddate 120821000000Z -out server-expired.crt -infiles server-expired.csr # Valid client key and certificate. openssl genrsa -out client.key 1024 openssl req -new -key client.key -out client.csr -config openssl.cnf -subj "${SBASESUBJ}/CN=test client/" -openssl ca -config openssl.cnf -name CA_signing -out client.crt -infiles client.csr +openssl ca -batch -config openssl.cnf -name CA_signing -out client.crt -infiles client.csr # Expired client certificate, based on the above client key. openssl req -new -days 1 -key client.key -out client-expired.csr -config openssl.cnf -subj "${SBASESUBJ}/CN=test client expired/" -openssl ca -config openssl.cnf -name CA_signing -days 1 -startdate 120820000000Z -enddate 120821000000Z -out client-expired.crt -infiles client-expired.csr +openssl ca -batch -config openssl.cnf -name CA_signing -days 1 -startdate 120820000000Z -enddate 120821000000Z -out client-expired.crt -infiles client-expired.csr # Revoked client certificate, based on a new client key. openssl genrsa -out client-revoked.key 1024 openssl req -new -days 1 -key client-revoked.key -out client-revoked.csr -config openssl.cnf -subj "${SBASESUBJ}/CN=test client revoked/" -openssl ca -config openssl.cnf -name CA_signing -out client-revoked.crt -infiles client-revoked.csr -openssl ca -config openssl.cnf -name CA_signing -revoke client-revoked.crt -openssl ca -config openssl.cnf -name CA_signing -gencrl -out crl.pem +openssl ca -batch -config openssl.cnf -name CA_signing -out client-revoked.crt -infiles client-revoked.csr +openssl ca -batch -config openssl.cnf -name CA_signing -revoke client-revoked.crt +openssl ca -batch -config openssl.cnf -name CA_signing -gencrl -out crl.pem # Valid client key and certificate, encrypted (use "password" as password) -openssl genrsa -des3 -out client-encrypted.key 1024 -openssl req -new -key client-encrypted.key -out client-encrypted.csr -config openssl.cnf -subj "${SBASESUBJ}/CN=test client encrypted/" -openssl ca -config openssl.cnf -name CA_signing -out client-encrypted.crt -infiles client-encrypted.csr +openssl genrsa -des3 -out client-encrypted.key -passout pass:password 1024 +openssl req -new -key client-encrypted.key -out client-encrypted.csr -config openssl.cnf -subj "${SBASESUBJ}/CN=test client encrypted/" -passin pass:password +openssl ca -batch -config openssl.cnf -name CA_signing -out client-encrypted.crt -infiles client-encrypted.csr cat test-signing-ca.crt test-root-ca.crt > all-ca.crt #mkdir certs