Merge branch 'fixes'

This commit is contained in:
Roger A. Light 2023-08-16 14:03:29 +01:00
commit 9e0831b3ed
81 changed files with 1570 additions and 550 deletions

81
.github/workflows/windows.yml vendored Normal file
View File

@ -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

1
.gitignore vendored
View File

@ -82,6 +82,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/

View File

@ -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/")

View File

@ -1,3 +1,62 @@
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-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.
2.0.15 - 2022-08-16
===================

View File

@ -82,7 +82,3 @@ Equivalent options for enabling/disabling features are available when using the
## Credits
Mosquitto was written by Roger Light <roger@atchoo.org>
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)

View File

@ -16,7 +16,6 @@ Contributors:
Roger Light - initial implementation and documentation.
*/
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>

View File

@ -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

View File

@ -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

View File

@ -23,6 +23,8 @@ Contributors:
#include <string.h>
#ifndef WIN32
# include <errno.h>
# include <fcntl.h>
# include <strings.h>
#endif
@ -30,6 +32,7 @@ Contributors:
#include "mosquitto.h"
#include "password_mosq.h"
#include "get_password.h"
#include "misc_mosq.h"
void dynsec__print_usage(void)
{
@ -738,13 +741,6 @@ static int dynsec_init(int argc, char *argv[])
admin_password = password;
}
fptr = fopen(filename, "rb");
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");
@ -753,7 +749,17 @@ static int dynsec_init(int argc, char *argv[])
json_str = cJSON_Print(tree);
cJSON_Delete(tree);
fptr = fopen(filename, "wb");
#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);

View File

@ -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();

View File

@ -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;
fbackup = fopen(backup_file, "wt");
#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);
@ -599,7 +611,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,20 +622,20 @@ 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);
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;

View File

@ -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();

View File

@ -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;

View File

@ -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

View File

@ -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)
@ -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,

View File

@ -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"

View File

@ -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"

View File

@ -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;
}

View File

@ -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){

View File

@ -22,6 +22,8 @@ Contributors:
#include "config.h"
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@ -33,8 +35,12 @@ Contributors:
# include <io.h>
# include <lmcons.h>
# include <fcntl.h>
# define PATH_MAX MAX_PATH
#else
# include <sys/stat.h>
# include <pwd.h>
# include <grp.h>
# include <unistd.h>
#endif
#include "misc_mosq.h"
@ -126,30 +132,87 @@ 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(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
}

View File

@ -61,8 +61,11 @@ int mosquitto_lib_init(void)
srand((unsigned int)GetTickCount64());
#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
srand((unsigned int)tp.tv_nsec);
#elif defined(__APPLE__)
uint64_t ticks;
@ -329,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;
}

View File

@ -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);

View File

@ -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){
@ -228,19 +230,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;
}
@ -286,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;

View File

@ -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{
@ -268,6 +283,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;
}
@ -376,7 +393,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

View File

@ -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;
}
@ -1248,7 +1252,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 +1272,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;

View File

@ -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;
}

View File

@ -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:

View File

@ -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;

View File

@ -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<len-1; i++){
if(hostname[i] == '.'){
dotcount++;
}
}
if(dotcount < 1){
/* Exclude e.g. *.com, allow e.g. *.example.com */
return 1;
}
return strcasecmp(certname, hostname);
}else{
return strcasecmp(certname, hostname);

View File

@ -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 ${PROJECT_SOURCE_DIR}/man/${page}
COMMAND xsltproc ${PROJECT_SOURCE_DIR}/man/${page}.xml -o ${PROJECT_SOURCE_DIR}/man/
MAIN_DEPENDENCY ${PROJECT_SOURCE_DIR}/man/${page}.xml)
add_custom_target(${page} ALL DEPENDS ${PROJECT_SOURCE_DIR}/man/${page})
endfunction()
find_program(XSLTPROC xsltproc OPTIONAL)
if(XSLTPROC)
function(compile_manpage page)
add_custom_command(OUTPUT ${PROJECT_SOURCE_DIR}/man/${page}
COMMAND xsltproc ${PROJECT_SOURCE_DIR}/man/${page}.xml -o ${PROJECT_SOURCE_DIR}/man/
MAIN_DEPENDENCY ${PROJECT_SOURCE_DIR}/man/${page}.xml)
add_custom_target(${page} ALL DEPENDS ${PROJECT_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)

View File

@ -47,7 +47,7 @@
<title>Server</title>
<para>Generate a server key.</para>
<itemizedlist mark="circle">
<listitem><para>openssl genrsa -des3 -out server.key 2048</para></listitem>
<listitem><para>openssl genrsa -aes256 -out server.key 2048</para></listitem>
</itemizedlist>
<para>Generate a server key without encryption.</para>
@ -71,7 +71,7 @@
<title>Client</title>
<para>Generate a client key.</para>
<itemizedlist mark="circle">
<listitem><para>openssl genrsa -des3 -out client.key 2048</para></listitem>
<listitem><para>openssl genrsa -aes256 -out client.key 2048</para></listitem>
</itemizedlist>
<para>Generate a certificate signing request to send to the CA.</para>

View File

@ -1391,9 +1391,12 @@ openssl dhparam -out dhparam.pem 2048</programlisting>
<term><option>keyfile</option> <replaceable>file path</replaceable></term>
<listitem>
<para>
Path to the PEM encoded server key. This
option and <option>certfile</option> must be present
to enable certificate based TLS encryption.
If <option>tls_keyform</option> equals "pem" this is the
path to the PEM encoded server key. This option
and <option>certfile</option> must be present
to enable certificate based TLS encryption. If
<option>tls_keyform</option> is "engine" this represents
the engine handle of the private key.
</para>
<para>
The private key pointed to by this option will be
@ -1458,7 +1461,7 @@ openssl dhparam -out dhparam.pem 2048</programlisting>
<replaceable>tlsv1.3</replaceable>,
<replaceable>tlsv1.2</replaceable> and
<replaceable>tlsv1.1</replaceable>. If left unset,
the default of allowing TLS v1.3 and v1.2.</para>
the default allows TLS v1.3 and v1.2.</para>
<para>In Mosquitto version 1.6.x and earlier, this
option set the only TLS protocol version that
was allowed, rather than the minimum.</para>

View File

@ -639,6 +639,16 @@
Defaults to <option>5</option>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-W</option></term>
<listitem>
<para>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.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--will-payload</option></term>
<listitem>

View File

@ -938,7 +938,6 @@ mosquitto_sub -t 'bbc/#' -T bbc/bbc1 --remove-retained</programlisting>
<para>If the payload is not valid JSON, then the error message "Error: Message payload is not valid JSON on topic
&lt;topic&gt;" will be printed to stderr.</para></listitem>
<listitem><para><option>%I</option> ISO-8601 format date and time, e.g. 2016-08-10T09:47:38+0100</para></listitem>
<listitem><para><option>%U</option> Unix timestamp with nanoseconds, e.g. 1470818943.786368637</para></listitem>
</itemizedlist>
</refsect2>

View File

@ -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

View File

@ -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);
}

View File

@ -61,7 +61,7 @@ Command:
{
"commands":[
{
"command": "getDefaultACLAccess",
"command": "getDefaultACLAccess"
}
]
}
@ -244,7 +244,7 @@ Command:
mosquitto_ctrl example:
```
mosquitto_ctrl dynsec setClientPassword username password
mosquitto_ctrl dynsec setClientId username clientId
```
## Set Client Password
@ -523,7 +523,7 @@ Command:
{
"commands":[
{
"command": "getAnonymousGroup",
"command": "getAnonymousGroup"
}
]
}

View File

@ -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<len; i++){
if( ((char *)a)[i] != ((char *)b)[i] ){
rc = 1;
}
rc |= ((char *)a)[i] ^ ((char *)b)[i];
}
return rc;
}

View File

@ -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){
@ -126,47 +124,46 @@ 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 */
jtmp = cJSON_GetObjectItem(j_client, "username");
if(jtmp == NULL || !cJSON_IsString(jtmp)){
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 +173,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 +188,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 +200,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 +213,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 +231,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 +327,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 +439,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 +732,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 +814,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 +834,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);
}
}
}

View File

@ -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");
@ -214,26 +214,31 @@ 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", &str, 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 +248,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 +266,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 +285,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 +298,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 +930,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 +953,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 +963,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 +999,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 +1022,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);
}
}
}

View File

@ -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 <winsock2.h>
# include <aclapi.h>
# include <io.h>
# include <lmcons.h>
# include <fcntl.h>
# define PATH_MAX MAX_PATH
#else
# include <sys/stat.h>
# include <pwd.h>
# include <grp.h>
# include <unistd.h>
#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;
@ -137,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");
@ -151,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");
}
}
@ -292,37 +474,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;
}
@ -359,7 +518,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;
@ -451,7 +610,7 @@ void dynsec__config_save(void)
json_str_len = strlen(json_str);
/* Save to file */
file_path_len = strlen(config_file) + 1;
file_path_len = strlen(config_file) + strlen(".new") + 1;
file_path = mosquitto_malloc(file_path_len);
if(file_path == NULL){
mosquitto_free(json_str);
@ -460,7 +619,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);

View File

@ -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{

View File

@ -215,14 +215,24 @@ 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;
char *topic;
json_get_string(j_acl, "acltype", &acltype, false);
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;
@ -231,16 +241,12 @@ 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);
}
jtmp = cJSON_GetObjectItem(j_acl, "topic");
if(jtmp && cJSON_IsString(jtmp)){
acl->topic = mosquitto_strdup(jtmp->valuestring);
bool allow;
if(json_get_bool(j_acl, "allow", &allow, false, false) == MOSQ_ERR_SUCCESS){
acl->allow = allow;
}
acl->topic = mosquitto_strdup(topic);
if(acl->topic == NULL){
mosquitto_free(acl);
continue;
@ -255,7 +261,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");
@ -269,27 +275,31 @@ 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 */
jtmp = cJSON_GetObjectItem(j_role, "rolename");
if(jtmp == NULL){
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 +308,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 +604,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 +624,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 +662,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 +690,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 +702,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 +721,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 +767,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);

View File

@ -2,7 +2,7 @@
MAJOR=2
MINOR=0
REVISION=15
REVISION=16
sed -i "s/^VERSION=.*/VERSION=${MAJOR}.${MINOR}.${REVISION}/" config.mk

View File

@ -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.

View File

@ -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){

View File

@ -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;
@ -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;

View File

@ -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);
@ -214,19 +223,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);

View File

@ -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){

View File

@ -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;
@ -790,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);
@ -936,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);
@ -946,7 +962,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

View File

@ -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;
}

View File

@ -20,6 +20,7 @@ Contributors:
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#ifndef WIN32
#include <syslog.h>
#endif
@ -129,11 +130,16 @@ 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){
setvbuf(stdout, NULL, _IOLBF, 0);
}
#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;
@ -291,7 +297,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] = ':';

View File

@ -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
@ -475,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));

View File

@ -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);

View File

@ -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"
@ -295,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);
@ -343,7 +348,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);
@ -416,7 +421,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;
@ -479,7 +484,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);
@ -622,6 +627,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 +639,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 +770,16 @@ 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){
freeaddrinfo(ainfo);
return rc;
}else{
continue;
}
}
interface_bound = true;
}

View File

@ -201,9 +201,7 @@ int pw__memcmp_const(const void *a, const void *b, size_t len)
if(!a || !b) return 1;
for(i=0; i<len; i++){
if( ((char *)a)[i] != ((char *)b)[i] ){
rc = 1;
}
rc |= ((char *)a)[i] ^ ((char *)b)[i];
}
return rc;
}

View File

@ -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){

View File

@ -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;

View File

@ -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){

View File

@ -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,14 +164,17 @@ 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){
if(context && (context->clean_start == false
memset(&chunk, 0, sizeof(struct P_client));
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;
@ -221,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;
@ -235,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);
@ -278,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){

View File

@ -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{

View File

@ -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;
}

View File

@ -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;

View File

@ -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);
@ -980,9 +980,7 @@ static int mosquitto__memcmp_const(const void *a, const void *b, size_t len)
if(!a || !b) return 1;
for(i=0; i<len; i++){
if( ((char *)a)[i] != ((char *)b)[i] ){
rc = 1;
}
rc |= ((char *)a)[i] ^ ((char *)b)[i];
}
return rc;
}

View File

@ -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)
}
}
}

View File

@ -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;
}
}

View File

@ -23,6 +23,7 @@ Contributors:
#include <math.h>
#include <stdio.h>
#include <limits.h>
#include <inttypes.h>
#include "mosquitto_broker_internal.h"
#include "memory_mosq.h"
@ -76,31 +77,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 +117,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 +136,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;
@ -217,8 +218,8 @@ 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);
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);
initial_publish = false;
@ -287,32 +288,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 +323,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;

View File

@ -0,0 +1,81 @@
#!/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()
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}")
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)

View File

@ -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()

View File

@ -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)

View File

@ -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):

51
test/broker/07-will-control.py Executable file
View File

@ -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)

View File

@ -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
@ -83,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
@ -90,6 +92,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
@ -124,6 +127,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

View File

@ -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'),
@ -63,11 +64,13 @@ 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'),
(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'),
@ -100,6 +103,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'),

View File

@ -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:

View File

@ -82,6 +82,10 @@ PERSIST_WRITE_OBJS = \
utf8_mosq.o \
util_mosq.o
TLS_TEST_OBJS = \
tls_test.o \
tls_stubs.o
SUBS_TEST_OBJS = \
subs_test.o \
subs_stubs.o
@ -112,6 +116,9 @@ persist_write_test : ${PERSIST_WRITE_TEST_OBJS} ${PERSIST_WRITE_OBJS}
subs_test : ${SUBS_TEST_OBJS} ${SUBS_OBJS}
$(CROSS_COMPILE)$(CC) $(LDFLAGS) -o $@ $^ $(LDADD)
tls_test : ${TLS_TEST_OBJS} ${TLS_OBJS}
$(CROSS_COMPILE)$(CC) $(LDFLAGS) -o $@ $^ $(LDADD) -lssl -lcrypto
bridge_topic.o : ../../src/bridge_topic.c
$(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -DWITH_BROKER -DWITH_BRIDGE -c -o $@ $^
@ -167,10 +174,11 @@ util_topic.o : ../../lib/util_topic.c
utf8_mosq.o : ../../lib/utf8_mosq.c
$(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $^
build : mosq_test bridge_topic_test persist_read_test persist_write_test subs_test
build : mosq_test bridge_topic_test persist_read_test persist_write_test subs_test tls_test
test-lib : build
./mosq_test
./tls_test
test-broker : build
./bridge_topic_test

40
test/unit/tls_stubs.c Normal file
View File

@ -0,0 +1,40 @@
#include "config.h"
#include <time.h>
#include <logging_mosq.h>
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;
}

102
test/unit/tls_test.c Normal file
View File

@ -0,0 +1,102 @@
#include <CUnit/CUnit.h>
#include <CUnit/Basic.h>
#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;
}

View File

@ -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);
}

View File

@ -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**.
@ -62,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/
@ -81,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

View File

@ -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/