Merge branch 'master' into develop

This commit is contained in:
Roger A. Light
2022-08-17 16:18:24 +01:00
95 changed files with 1340 additions and 282 deletions
-35
View File
@@ -1,35 +0,0 @@
# Configuration for Lock Threads - https://github.com/dessant/lock-threads
# Number of days of inactivity before a closed issue or pull request is locked
daysUntilLock: 90
# Skip issues and pull requests created before a given timestamp. Timestamp must
# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable
skipCreatedBefore: false
# Issues and pull requests with these labels will be ignored. Set to `[]` to disable
exemptLabels: []
# Label to add before locking, such as `outdated`. Set to `false` to disable
lockLabel: false
# Comment to post before locking. Set to `false` to disable
lockComment: false
# Assign `resolved` as the reason for locking. Set to `false` to disable
setLockReason: true
# Limit to only `issues` or `pulls`
only: issues
# Optionally, specify configuration settings just for `issues` or `pulls`
# issues:
# exemptLabels:
# - help-wanted
# lockLabel: outdated
# pulls:
# daysUntilLock: 30
# Repository to extend settings from
# _extends: repo
@@ -0,0 +1,26 @@
name: Coverity Scan develop branch on a weekly basis
on:
workflow_dispatch:
schedule:
- cron: "7 3 * * 0"
jobs:
coverity:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: develop
- name: Dependencies
run: sudo apt-get install -y libcjson-dev libsqlite3-dev libssl-dev uthash-dev
- uses: vapier/coverity-scan-action@v1
with:
build_language: 'cxx'
project: "eclipse/mosquitto"
token: ${{ secrets.COVERITY_SCAN_TOKEN }}
email: ${{ secrets.COVERITY_SCAN_EMAIL }}
command: "make binary"
+26
View File
@@ -0,0 +1,26 @@
name: Coverity Scan fixes branch on a weekly basis
on:
workflow_dispatch:
schedule:
- cron: "7 3 * * 3"
jobs:
coverity:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: fixes
- name: Dependencies
run: sudo apt-get install -y libcjson-dev libsqlite3-dev libssl-dev uthash-dev
- uses: vapier/coverity-scan-action@v1
with:
build_language: 'cxx'
project: "eclipse/mosquitto"
token: ${{ secrets.COVERITY_SCAN_TOKEN }}
email: ${{ secrets.COVERITY_SCAN_EMAIL }}
command: "make binary"
+21
View File
@@ -0,0 +1,21 @@
name: 'Lock Threads'
on:
schedule:
- cron: '0 0 * * 0'
workflow_dispatch:
permissions:
issues: write
pull-requests: write
concurrency:
group: lock
jobs:
action:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v3
with:
issue-inactive-days: '90'
+1 -1
View File
@@ -13,7 +13,7 @@ project(mosquitto
LANGUAGES C CXX
)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/")
list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/")
add_definitions (-DCMAKE -DVERSION=\"${VERSION}\")
+70
View File
@@ -161,6 +161,73 @@ Build:
- Add an CMake option `WITH_LTO` to enable/disable link time optimization.
2.0.15 - 2022-08-16
===================
Security:
- Deleting the group configured as the anonymous group in the Dynamic Security
plugin, would leave a dangling pointer that could lead to a single crash.
This is considered a minor issue - only administrative users should have
access to dynsec, the impact on availability is one-off, and there is no
associated loss of data. It is now forbidden to delete the group configured
as the anonymous group.
Broker:
- Fix memory leak when a plugin modifies the topic of a message in
MOSQ_EVT_MESSAGE.
- Fix bridge `restart_timeout` not being honoured.
- Fix potential memory leaks if a plugin modifies the message in the
MOSQ_EVT_MESSAGE event.
- Fix unused flags in CONNECT command being forced to be 0, which is not
required for MQTT v3.1. Closes #2522.
- Improve documentation of `persistent_client_expiration` option.
Closes #2404.
- Add clients to session expiry check list when restarting and reloading from
persistence. Closes #2546.
- Fix bridges not sending failure notification messages to the local broker if
the remote bridge connection fails. Closes #2467. Closes #1488.
- Fix some PUBLISH messages not being counted in $SYS stats. Closes #2448.
- Fix incorrect return code being sent in DISCONNECT when a client session is
taken over. Closes #2607.
- Fix confusing "out of memory" error when a client is kicked in the dynamic
security plugin. Closes #2525.
- Fix confusing error message when dynamic security config file was a
directory. Closes #2520.
- Fix bridge queued messages not being persisted when local_cleansession is
set to false and cleansession is set to true. Closes #2604.
- Dynamic security: Fix modifyClient and modifyGroup commands to not modify
the client/group if a new group/client being added is not valid.
Closes #2598.
- Dynamic security: Fix the plugin being able to be loaded twice. Currently
only a single plugin can interact with a unique $CONTROL topic. Using
multiple instances of the plugin would produce duplicate entries in the
config file. Closes #2601. Closes #2470.
- Fix case where expired messages were causing queued messages not to be
delivered. Closes #2609.
- Fix websockets not passing on the X-Forwarded-For header.
Client library:
- Fix threads library detection on Windows under cmake. Bumps the minimum
cmake version to 3.1, which is still ancient.
- Fix use of `MOSQ_OPT_TLS_ENGINE` being unable to be used due to the openssl
ctx not being initialised until starting to connect. Closes #2537.
- Fix incorrect use of SSL_connect. Closes #2594.
- Don't set SIGPIPE to ignore, use MSG_NOSIGNAL instead. Closes #2564.
- Add documentation of struct mosquitto_message to header. Closes #2561.
- Fix documentation omission around mosquitto_reinitialise. Closes #2489.
- Fix use of MOSQ_OPT_SSL_CTX when used in conjunction with
MOSQ_OPT_SSL_CTX_DEFAULTS. Closes #2463.
- Fix failure to close thread in some situations. Closes #2545.
Clients:
- Fix mosquitto_pub incorrectly reusing topic aliases when reconnecting.
Closes #2494.
Apps:
- Fix `-o` not working in `mosquitto_ctrl`, and typo in related documentation.
Closes #2471.
2.0.14 - 2021-11-17
===================
@@ -188,6 +255,9 @@ Broker:
- Fix broker sending duplicate CONNACK on failed MQTT v5 reauthentication.
Closes #2339.
- Fix mosquitto_plugin.h not including mosquitto_broker.h. Closes #2350.
- Fix unlimited message quota not being properly checked for incoming
messages. Closes #2593.
- Fixed build for openssl compiled with OPENSSL_NO_ENGINE. Closes #2589.
Client library:
- Initialise sockpairR/W to invalid in `mosquitto_reinitialise()` to avoid
+7
View File
@@ -152,3 +152,10 @@ void db__msg_add_to_queued_stats(struct mosquitto_msg_data *msg_data, struct mos
UNUSED(msg_data);
UNUSED(msg);
}
int session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time)
{
UNUSED(context);
UNUSED(expiry_time);
return 0;
}
+4
View File
@@ -22,6 +22,10 @@ Contributors:
#include <stdlib.h>
#include <string.h>
#ifndef WIN32
# include <strings.h>
#endif
#include "mosquitto_ctrl.h"
#include "mosquitto.h"
#include "base64_mosq.h"
+4
View File
@@ -22,6 +22,10 @@ Contributors:
#include <stdlib.h>
#include <string.h>
#ifndef WIN32
# include <strings.h>
#endif
#include "mosquitto.h"
#include "mosquitto_ctrl.h"
#include "password_mosq.h"
+4
View File
@@ -22,6 +22,10 @@ Contributors:
#include <stdlib.h>
#include <string.h>
#ifndef WIN32
# include <strings.h>
#endif
#include "mosquitto_ctrl.h"
void ctrl_help(void)
+4
View File
@@ -24,6 +24,10 @@ Contributors:
#include <stdlib.h>
#include <string.h>
#ifndef WIN32
# include <strings.h>
#endif
#include "lib_load.h"
#include "mosquitto.h"
#include "mosquitto_ctrl.h"
+5 -4
View File
@@ -90,13 +90,14 @@ int ctrl_config_parse(struct mosq_config *cfg, int *argc, char **argv[])
init_config(cfg);
rc = client_config_load(cfg);
if(rc) return rc;
/* Deal with real argc/argv */
rc = client_config_line_proc(cfg, argc, argv);
if(rc) return rc;
/* Load options from config file - this must be after `-o` has been processed */
rc = client_config_load(cfg);
if(rc) return rc;
#ifdef WITH_TLS
if((cfg->certfile && !cfg->keyfile) || (cfg->keyfile && !cfg->certfile)){
fprintf(stderr, "Error: Both certfile and keyfile must be provided if one of them is set.\n");
@@ -541,7 +542,7 @@ int client_config_load(struct mosq_config *cfg)
fclose(fptr);
return 1;
}
while(fgets(line, 1024, fptr)){
while(fgets(line, sizeof(line), fptr)){
if(line[0] == '#') continue; /* Comments */
while(line[strlen(line)-1] == 10 || line[strlen(line)-1] == 13){
+1
View File
@@ -135,6 +135,7 @@ void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flag
connack_result = result;
if(!result){
first_publish = true;
switch(cfg.pub_mode){
case MSGMODE_CMD:
case MSGMODE_FILE:
+1
View File
@@ -272,6 +272,7 @@ ifeq ($(WITH_THREADING),yes)
BROKER_CFLAGS:=$(BROKER_CFLAGS) -pthread
BROKER_LDFLAGS:=$(BROKER_LDFLAGS) -pthread
LIB_CFLAGS:=$(LIB_CFLAGS) -pthread
LIB_LDFLAGS:=$(LIB_LDFLAGS) -pthread
LIB_CPPFLAGS:=$(LIB_CPPFLAGS) -DWITH_THREADING
LIB_LDFLAGS:=$(LIB_LDFLAGS) -pthread
CLIENT_CFLAGS:=$(CLIENT_CFLAGS) -pthread
+2 -3
View File
@@ -1,4 +1,4 @@
FROM alpine:3.12
FROM alpine:3.14
LABEL maintainer="Roger Light <roger@atchoo.org>" \
description="Eclipse Mosquitto MQTT Broker"
@@ -40,9 +40,8 @@ RUN set -x && \
export GNUPGHOME="$(mktemp -d)" && \
found=''; \
for server in \
ha.pool.sks-keyservers.net \
htps://keys.openpgp.org \
hkp://keyserver.ubuntu.com:80 \
hkp://p80.pool.sks-keyservers.net:80 \
pgp.mit.edu \
; do \
echo "Fetching GPG key $GPG_KEYS from $server"; \
+4 -5
View File
@@ -42,9 +42,8 @@ RUN set -x && \
export GNUPGHOME="$(mktemp -d)" && \
found=''; \
for server in \
ha.pool.sks-keyservers.net \
hkps://keys.openpgp.org \
hkp://keyserver.ubuntu.com:80 \
hkp://p80.pool.sks-keyservers.net:80 \
pgp.mit.edu \
; do \
echo "Fetching GPG key $GPG_KEYS from $server"; \
@@ -65,7 +64,7 @@ RUN set -x && \
WITH_SHARED_LIBRARIES=yes \
WITH_SRV=no \
WITH_STRIP=yes \
WITH_TLS_PSK=no \
WITH_TLS_PSK=no \
WITH_WEBSOCKETS=yes \
prefix=/usr \
binary && \
@@ -82,8 +81,8 @@ RUN set -x && \
chown -R mosquitto:mosquitto /mosquitto && \
apk --no-cache add \
ca-certificates \
libressl \
libuuid && \
libressl \
libuuid && \
apk del build-deps && \
rm -rf /build
+1 -2
View File
@@ -45,9 +45,8 @@ RUN set -x && \
export GNUPGHOME="$(mktemp -d)" && \
found=''; \
for server in \
ha.pool.sks-keyservers.net \
hkps://keys.openpgp.org \
hkp://keyserver.ubuntu.com:80 \
hkp://p80.pool.sks-keyservers.net:80 \
pgp.mit.edu \
; do \
echo "Fetching GPG key $GPG_KEYS from $server"; \
+1 -2
View File
@@ -45,9 +45,8 @@ RUN set -x && \
export GNUPGHOME="$(mktemp -d)" && \
found=''; \
for server in \
ha.pool.sks-keyservers.net \
hkps://keys.openpgp.org \
hkp://keyserver.ubuntu.com:80 \
hkp://p80.pool.sks-keyservers.net:80 \
pgp.mit.edu \
; do \
echo "Fetching GPG key $GPG_KEYS from $server"; \
+4 -5
View File
@@ -1,10 +1,10 @@
FROM alpine:3.14
FROM alpine:3.16
LABEL maintainer="Roger Light <roger@atchoo.org>" \
description="Eclipse Mosquitto MQTT Broker"
ENV VERSION=2.0.14 \
DOWNLOAD_SHA256=d0dde8fdb12caf6e2426b4f28081919a2fce3448773bdb8af0d3cd5fe5776925 \
ENV VERSION=2.0.15 \
DOWNLOAD_SHA256=4735b1d32e3f91c7a8896741d88a3022e89730a1ee897946decfa0df27039ac6 \
GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7 \
LWS_VERSION=4.2.1 \
LWS_SHA256=842da21f73ccba2be59e680de10a8cce7928313048750eb6ad73b6fa50763c51
@@ -46,9 +46,8 @@ RUN set -x && \
export GNUPGHOME="$(mktemp -d)" && \
found=''; \
for server in \
ha.pool.sks-keyservers.net \
hkps://keys.openpgp.org \
hkp://keyserver.ubuntu.com:80 \
hkp://p80.pool.sks-keyservers.net:80 \
pgp.mit.edu \
; do \
echo "Fetching GPG key $GPG_KEYS from $server"; \
+4 -5
View File
@@ -1,10 +1,10 @@
FROM alpine:3.14
FROM alpine:3.16
LABEL maintainer="Roger Light <roger@atchoo.org>" \
description="Eclipse Mosquitto MQTT Broker"
ENV VERSION=2.0.14 \
DOWNLOAD_SHA256=d0dde8fdb12caf6e2426b4f28081919a2fce3448773bdb8af0d3cd5fe5776925 \
ENV VERSION=2.0.15 \
DOWNLOAD_SHA256=4735b1d32e3f91c7a8896741d88a3022e89730a1ee897946decfa0df27039ac6 \
GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7 \
LWS_VERSION=4.2.1 \
LWS_SHA256=842da21f73ccba2be59e680de10a8cce7928313048750eb6ad73b6fa50763c51
@@ -46,9 +46,8 @@ RUN set -x && \
export GNUPGHOME="$(mktemp -d)" && \
found=''; \
for server in \
ha.pool.sks-keyservers.net \
hkps://keys.openpgp.org \
hkp://keyserver.ubuntu.com:80 \
hkp://p80.pool.sks-keyservers.net:80 \
pgp.mit.edu \
; do \
echo "Fetching GPG key $GPG_KEYS from $server"; \
+1 -2
View File
@@ -63,9 +63,8 @@ RUN set -x && \
export GNUPGHOME="$(mktemp -d)" && \
found=''; \
for server in \
ha.pool.sks-keyservers.net \
hkps://keys.openpgp.org \
hkp://keyserver.ubuntu.com:80 \
hkp://p80.pool.sks-keyservers.net:80 \
pgp.mit.edu \
; do \
echo "Fetching GPG key $GPG_KEYS from $server"; \
+33 -5
View File
@@ -85,7 +85,8 @@ extern "C" {
#define MOSQ_LOG_INTERNAL 0x80000000U
#define MOSQ_LOG_ALL 0xFFFFFFFFU
/* Error values */
/* Enum: mosq_err_t
* Integer values returned from many libmosquitto functions. */
enum mosq_err_t {
MOSQ_ERR_QUOTA_EXCEEDED = -6,
MOSQ_ERR_AUTH_DELAYED = -5,
@@ -142,7 +143,12 @@ enum mosq_err_t {
MOSQ_ERR_CONNECTION_RATE_EXCEEDED = 159,
};
/* Option values */
/* Enum: mosq_opt_t
*
* Client options.
*
* See <mosquitto_int_option>, <mosquitto_string_option>, and <mosquitto_void_option>.
*/
enum mosq_opt_t {
MOSQ_OPT_PROTOCOL_VERSION = 1,
MOSQ_OPT_SSL_CTX = 2,
@@ -175,6 +181,24 @@ enum mosq_transport_t {
#define MQTT_PROTOCOL_V311 4
#define MQTT_PROTOCOL_V5 5
/* Struct: mosquitto_message
*
* Contains details of a PUBLISH message.
*
* int mid - the message/packet ID of the PUBLISH message, assuming this is a
* QoS 1 or 2 message. Will be set to 0 for QoS 0 messages.
*
* char *topic - the topic the message was delivered on.
*
* void *payload - the message payload. This will be payloadlen bytes long, and
* may be NULL if a zero length payload was sent.
*
* int payloadlen - the length of the payload, in bytes.
*
* int qos - the quality of service of the message, 0, 1, or 2.
*
* bool retain - set to true for stale retained messages.
*/
struct mosquitto_message{
int mid;
char *topic;
@@ -359,9 +383,10 @@ libmosq_EXPORT void mosquitto_destroy(struct mosquitto *mosq);
* callbacks that are specified.
*
* Returns:
* MOSQ_ERR_SUCCESS - on success.
* MOSQ_ERR_INVAL - if the input parameters were invalid.
* MOSQ_ERR_NOMEM - if an out of memory condition occurred.
* MOSQ_ERR_SUCCESS - on success.
* MOSQ_ERR_INVAL - if the input parameters were invalid.
* MOSQ_ERR_NOMEM - if an out of memory condition occurred.
* MOSQ_ERR_MALFORMED_UTF8 - if the client id is not valid UTF-8.
*
* See Also:
* <mosquitto_new>, <mosquitto_destroy>
@@ -1623,6 +1648,9 @@ libmosq_EXPORT int mosquitto_int_option(struct mosquitto *mosq, enum mosq_opt_t
* MOSQ_OPT_TLS_ENGINE - Configure the client for TLS Engine support.
* Pass a TLS Engine ID to be used when creating TLS
* connections. Must be set before <mosquitto_connect>.
* Must be a valid engine, and note that the string will not be used
* until a connection attempt is made so this function will return
* success even if an invalid engine string is passed.
*
* MOSQ_OPT_TLS_KEYFORM - Configure the client to treat the keyfile
* differently depending on its type. Must be set
+2
View File
@@ -78,6 +78,7 @@ static int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int
mosq->msgs_in.inflight_quota = mosq->msgs_in.inflight_maximum;
mosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum;
mosq->retain_available = 1;
mosquitto__set_request_disconnect(mosq, false);
return MOSQ_ERR_SUCCESS;
}
@@ -265,6 +266,7 @@ int mosquitto_disconnect_v5(struct mosquitto *mosq, int reason_code, const mosqu
}
mosquitto__set_state(mosq, mosq_cs_disconnected);
mosquitto__set_request_disconnect(mosq, true);
if(!net__is_connected(mosq)){
return MOSQ_ERR_NO_CONN;
}else{
+4 -6
View File
@@ -108,9 +108,11 @@ int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets)
}
now = mosquitto_time();
pthread_mutex_lock(&mosq->msgtime_mutex);
if(mosq->next_msg_out && now + timeout_ms/1000 > mosq->next_msg_out){
timeout_ms = (mosq->next_msg_out - now)*1000;
}
pthread_mutex_unlock(&mosq->msgtime_mutex);
if(timeout_ms < 0){
/* There has been a delay somewhere which means we should have already
@@ -161,7 +163,6 @@ int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets)
FD_SET(mosq->sock, &writefds);
}
}
if(net__is_connected(mosq) && FD_ISSET(mosq->sock, &writefds)){
rc = mosquitto_loop_write(mosq, max_packets);
if(rc || !net__is_connected(mosq)){
@@ -242,7 +243,6 @@ int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets)
int run = 1;
int rc = MOSQ_ERR_SUCCESS;
unsigned long reconnect_delay;
enum mosquitto_client_state state;
if(!mosq) return MOSQ_ERR_INVAL;
@@ -281,8 +281,7 @@ int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets)
pthread_testcancel();
#endif
rc = MOSQ_ERR_SUCCESS;
state = mosquitto__get_state(mosq);
if(state == mosq_cs_disconnecting || state == mosq_cs_disconnected){
if(mosquitto__get_request_disconnect(mosq)){
run = 0;
}else{
if(mosq->reconnect_delay_max > mosq->reconnect_delay){
@@ -304,8 +303,7 @@ int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets)
rc = interruptible_sleep(mosq, (time_t)reconnect_delay);
if(rc) return rc;
state = mosquitto__get_state(mosq);
if(state == mosq_cs_disconnecting || state == mosq_cs_disconnected){
if(mosquitto__get_request_disconnect(mosq)){
run = 0;
}else{
rc = mosquitto_reconnect(mosq);
+3 -4
View File
@@ -109,10 +109,6 @@ struct mosquitto *mosquitto_new(const char *id, bool clean_start, void *userdata
return NULL;
}
#ifndef WIN32
signal(SIGPIPE, SIG_IGN);
#endif
mosq = (struct mosquitto *)mosquitto__calloc(1, sizeof(struct mosquitto));
if(mosq){
mosq->sock = INVALID_SOCKET;
@@ -179,6 +175,9 @@ int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_st
return MOSQ_ERR_MALFORMED_UTF8;
}
mosq->id = mosquitto__strdup(id);
if(!mosq->id){
return MOSQ_ERR_NOMEM;
}
}
packet__cleanup(&mosq->in_packet);
mosq->out_packet = NULL;
+2 -1
View File
@@ -404,8 +404,9 @@ struct mosquitto {
unsigned int reconnect_delay_max;
int callback_depth;
uint16_t port;
bool reconnect_exponential_backoff;
bool disable_socketpair;
bool reconnect_exponential_backoff;
bool request_disconnect;
char threaded;
struct mosquitto__packet *out_packet_last;
mosquitto_property *connect_properties;
+2 -6
View File
@@ -674,8 +674,8 @@ static int net__init_ssl_ctx(struct mosquitto *mosq)
* has not been set, or if both of MOSQ_OPT_SSL_CTX and
* MOSQ_OPT_SSL_CTX_WITH_DEFAULTS are set. */
if(mosq->tls_cafile || mosq->tls_capath || mosq->tls_psk || mosq->tls_use_os_certs){
net__init_tls();
if(!mosq->ssl_ctx){
net__init_tls();
#if OPENSSL_VERSION_NUMBER < 0x10100000L
mosq->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
@@ -1042,11 +1042,7 @@ ssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count)
/* Call normal write/send */
#endif
#ifndef WIN32
return write(mosq->sock, buf, count);
#else
return send(mosq->sock, buf, count, 0);
#endif
return send(mosq->sock, buf, count, MSG_NOSIGNAL);
#ifdef WITH_TLS
}
+5
View File
@@ -19,6 +19,7 @@ Contributors:
#define NET_MOSQ_H
#ifndef WIN32
# include <sys/socket.h>
# include <unistd.h>
#else
# include <winsock2.h>
@@ -51,6 +52,10 @@ typedef SSIZE_T ssize_t;
#define INVALID_SOCKET -1
#endif
#ifndef MSG_NOSIGNAL
# define MSG_NOSIGNAL 0
#endif
/* Macros for accessing the MSB and LSB of a uint16_t */
#define MOSQ_MSB(A) (uint8_t)((A & 0xFF00) >> 8)
#define MOSQ_LSB(A) (uint8_t)(A & 0x00FF)
+11 -8
View File
@@ -274,14 +274,17 @@ int mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt_t option, cons
switch(option){
case MOSQ_OPT_TLS_ENGINE:
#if defined(WITH_TLS) && !defined(OPENSSL_NO_ENGINE)
eng = ENGINE_by_id(value);
if(!eng){
return MOSQ_ERR_INVAL;
}
ENGINE_free(eng); /* release the structural reference from ENGINE_by_id() */
mosq->tls_engine = mosquitto__strdup(value);
if(!mosq->tls_engine){
return MOSQ_ERR_NOMEM;
mosquitto__free(mosq->tls_engine);
if(value){
eng = ENGINE_by_id(value);
if(!eng){
return MOSQ_ERR_INVAL;
}
ENGINE_free(eng); /* release the structural reference from ENGINE_by_id() */
mosq->tls_engine = mosquitto__strdup(value);
if(!mosq->tls_engine){
return MOSQ_ERR_NOMEM;
}
}
return MOSQ_ERR_SUCCESS;
#else
+1 -1
View File
@@ -558,7 +558,7 @@ int packet__read(struct mosquitto *mosq)
mosq->in_packet.pos = 0;
#ifdef WITH_BROKER
G_MSGS_RECEIVED_INC(1);
if(((mosq->in_packet.command)&0xF5) == CMD_PUBLISH){
if(((mosq->in_packet.command)&0xF0) == CMD_PUBLISH){
G_PUB_MSGS_RECEIVED_INC(1);
}
#endif
+1
View File
@@ -1206,6 +1206,7 @@ BROKER_EXPORT int mosquitto_property_copy_all(mosquitto_property **dest, const m
}
plast = pnew;
pnew->client_generated = src->client_generated;
pnew->identifier = src->identifier;
switch(pnew->identifier){
case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR:
+4
View File
@@ -21,6 +21,10 @@ Contributors:
#include <errno.h>
#include <string.h>
#ifndef WIN32
# include <strings.h>
#endif
#include "mosquitto.h"
#include "mqtt_protocol.h"
+25
View File
@@ -106,6 +106,11 @@ int mosquitto__check_keepalive(struct mosquitto *mosq)
pthread_mutex_unlock(&mosq->msgtime_mutex);
}else{
#ifdef WITH_BROKER
# ifdef WITH_BRIDGE
if(mosq->bridge){
context__send_will(mosq);
}
# endif
net__socket_close(mosq);
#else
net__socket_close(mosq);
@@ -288,3 +293,23 @@ enum mosquitto_client_state mosquitto__get_state(struct mosquitto *mosq)
return state;
}
#ifndef WITH_BROKER
void mosquitto__set_request_disconnect(struct mosquitto *mosq, bool request_disconnect)
{
pthread_mutex_lock(&mosq->state_mutex);
mosq->request_disconnect = request_disconnect;
pthread_mutex_unlock(&mosq->state_mutex);
}
bool mosquitto__get_request_disconnect(struct mosquitto *mosq)
{
bool request_disconnect;
pthread_mutex_lock(&mosq->state_mutex);
request_disconnect = mosq->request_disconnect;
pthread_mutex_unlock(&mosq->state_mutex);
return request_disconnect;
}
#endif
+4
View File
@@ -32,6 +32,10 @@ uint16_t mosquitto__mid_generate(struct mosquitto *mosq);
int mosquitto__set_state(struct mosquitto *mosq, enum mosquitto_client_state state);
enum mosquitto_client_state mosquitto__get_state(struct mosquitto *mosq);
#ifndef WITH_BROKER
void mosquitto__set_request_disconnect(struct mosquitto *mosq, bool request_disconnect);
bool mosquitto__get_request_disconnect(struct mosquitto *mosq);
#endif
#ifdef WITH_TLS
int mosquitto__hex2bin_sha1(const char *hex, unsigned char **bin);
+4 -4
View File
@@ -8,10 +8,10 @@ if(NOT WIN32)
find_program(XSLTPROC xsltproc OPTIONAL)
if(XSLTPROC)
function(compile_manpage page)
add_custom_command(OUTPUT "${CMAKE_SOURCE_DIR}/man/${page}"
COMMAND xsltproc "${CMAKE_SOURCE_DIR}/man/${page}.xml" -o "${CMAKE_SOURCE_DIR}/man/"
MAIN_DEPENDENCY "${CMAKE_SOURCE_DIR}/man/${page}.xml")
add_custom_target(${page} ALL DEPENDS "${CMAKE_SOURCE_DIR}/man/${page}")
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("libmosquitto.3")
+19 -10
View File
@@ -925,15 +925,21 @@ log_timestamp_format %Y-%m-%dT%H:%M:%S
<varlistentry>
<term><option>persistent_client_expiration</option> <replaceable>duration</replaceable></term>
<listitem>
<para>This option allows persistent clients (those with
clean session set to false) to be removed if they do
not reconnect within a certain time frame. This is a
non-standard option. As far as the MQTT spec is
concerned, persistent clients persist forever.</para>
<para>Badly designed clients may set clean session to false
whilst using a randomly generated client id. This leads
to persistent clients that will never reconnect. This
option allows these clients to be removed.</para>
<para>
This option allows the session of persistent clients (those with clean
session set to false) <emphasis>that are not currently connected</emphasis> to be removed if they
do not reconnect within a certain time frame. This is a non-standard option
in MQTT v3.1. MQTT v3.1.1 and v5.0 allow brokers to remove client sessions.
</para>
<para>
Badly designed clients may set clean session to false whilst using a randomly
generated client id. This leads to persistent clients that connect once and
never reconnect. This option allows these clients to be removed. This option
allows persistent clients (those with clean session set to false) to be
removed if they do not reconnect within a certain time frame.
</para>
<para>The expiration period should be an integer followed
by one of h d w m y for hour, day, week, month and year
respectively. For example:</para>
@@ -1043,7 +1049,9 @@ log_timestamp_format %Y-%m-%dT%H:%M:%S
<listitem>
<para>Set to <replaceable>true</replaceable> to queue
messages with QoS 0 when a persistent client is
disconnected. These messages are included in the limit
disconnected. When bridges topics are configured with QoS level 1 or 2 incoming
QoS 0 messages for these topics are also queued.
These messages are included in the limit
imposed by max_queued_messages. Defaults to
<replaceable>false</replaceable>.</para>
<para>Note that the MQTT v3.1.1 spec states that only QoS 1
@@ -1398,6 +1406,7 @@ accept_protocol_versions 3, 4</programlisting>
disconnected as not authorised when this option is
set to true. Do not use in conjunction with
<option>clientid_prefixes</option>.</para>
<para>This does not apply globally, but on a per-listener basis.</para>
<para>See also
<option>use_identity_as_username</option>.</para>
<para>Not reloaded on reload signal.</para>
+9 -6
View File
@@ -160,14 +160,16 @@
# accepted. MQTT imposes a maximum payload size of 268435455 bytes.
#message_size_limit 0
# This option allows persistent clients (those with clean session set to false)
# to be removed if they do not reconnect within a certain time frame.
#
# This is a non-standard option in MQTT V3.1 but allowed in MQTT v3.1.1.
# This option allows the session of persistent clients (those with clean
# session set to false) that are not currently connected to be removed if they
# do not reconnect within a certain time frame. This is a non-standard option
# in MQTT v3.1. MQTT v3.1.1 and v5.0 allow brokers to remove client sessions.
#
# Badly designed clients may set clean session to false whilst using a randomly
# generated client id. This leads to persistent clients that will never
# reconnect. This option allows these clients to be removed.
# generated client id. This leads to persistent clients that connect once and
# never reconnect. This option allows these clients to be removed. This option
# allows persistent clients (those with clean session set to false) to be
# removed if they do not reconnect within a certain time frame.
#
# The expiration period should be an integer followed by one of h d w m y for
# hour, day, week, month and year respectively. For example
@@ -324,6 +326,7 @@
# authorised when this option is set to true.
# Do not use in conjunction with clientid_prefixes.
# See also use_identity_as_username.
# This does not apply globally, but on a per-listener basis.
#use_username_as_clientid
# Change the websockets headers size. This is a global option, it is not
@@ -0,0 +1,24 @@
set (PLUGIN_NAME mosquitto_deny_protocol_version)
add_library(${PLUGIN_NAME} MODULE
${PLUGIN_NAME}.c
)
target_include_directories(${PLUGIN_NAME} PRIVATE
"${OPENSSL_INCLUDE_DIR}"
"${STDBOOL_H_PATH} ${STDINT_H_PATH}"
"${mosquitto_SOURCE_DIR}"
"${mosquitto_SOURCE_DIR}/include"
)
set_target_properties(${PLUGIN_NAME} PROPERTIES
PREFIX ""
POSITION_INDEPENDENT_CODE 1
)
if(WIN32)
target_link_libraries(${PLUGIN_NAME} PRIVATE mosquitto)
endif()
# Don't install, these are example plugins only.
#install(TARGETS ${PLUGIN_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+29
View File
@@ -0,0 +1,29 @@
R=../..
include ${R}/config.mk
.PHONY : all binary check clean reallyclean test install uninstall
PLUGIN_NAME=mosquitto_deny_protocol_version
PLUGIN_CFLAGS+=-I${R}/include -I${R}/
all : binary
binary : ${PLUGIN_NAME}.so
${PLUGIN_NAME}.so : ${PLUGIN_NAME}.c
$(CROSS_COMPILE)$(CC) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) $(PLUGIN_LDFLAGS) -fPIC -shared $< -o $@
reallyclean : clean
clean:
-rm -f *.o ${PLUGIN_NAME}.so *.gcda *.gcno
check: test
test:
install: ${PLUGIN_NAME}.so
# Don't install, these are examples only.
#$(INSTALL) -d "${DESTDIR}$(libdir)"
#$(INSTALL) ${STRIP_OPTS} ${PLUGIN_NAME}.so "${DESTDIR}${libdir}/${PLUGIN_NAME}.so"
uninstall :
-rm -f "${DESTDIR}${libdir}/${PLUGIN_NAME}.so"
@@ -0,0 +1,110 @@
/*
Copyright (c) 2022 Roger Light <roger@atchoo.org>
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
and Eclipse Distribution License v1.0 which accompany this distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: EPL-2.0 OR EDL-1.0
Contributors:
Roger Light - initial implementation and documentation.
*/
/*
* This is an example plugin showing how to deny access based on the version of
* the protocol spec a client connects with. It does no other authentication
* checks.
*
* It could be used with other authentication plugins by specifying it in the
* config file before another plugin, for example:
*
* plugin /usr/lib/mosquitto_deny_protocol_version.so
* plugin /usr/lib/mosquitto_dynamic_security.so
*
* or:
*
* plugin /usr/lib/mosquitto_deny_protocol_version.so
* password_file pwfile
*
* It will *not* work on its own.
*
* In Mosquitto 2.1, this can be achieved with the `accept_protocol_version`
* option instead.
*
*
* To compile:
*
* gcc -I<path to mosquitto-repo/include> -fPIC -shared mosquitto_deny_protocol_version.c -o mosquitto_deny_protocol_version.so
*
* Note that this only works on Mosquitto 2.0 or later.
*/
#include "config.h"
#include <stdio.h>
#include <string.h>
#include "mosquitto_broker.h"
#include "mosquitto_plugin.h"
#include "mosquitto.h"
#include "mqtt_protocol.h"
static mosquitto_plugin_id_t *mosq_pid = NULL;
int mosquitto_plugin_version(int supported_version_count, const int *supported_versions)
{
int i;
for(i=0; i<supported_version_count; i++){
if(supported_versions[i] == 5){
return 5;
}
}
return -1;
}
static int basic_auth_callback(int event, void *event_data, void *userdata)
{
struct mosquitto_evt_basic_auth *ed = event_data;
int protocol_version;
UNUSED(event);
UNUSED(userdata);
protocol_version = mosquitto_client_protocol_version(ed->client);
if(protocol_version == 5 || protocol_version == 4){
/* Allow access to MQTT v5.0 and v3.1.1 - this passes on responsibility
* for the actual auth checks to the next plugin/password file in the
* config list. If no other plugins/password file is defined, then
* access will be denied. */
return MOSQ_ERR_PLUGIN_DEFER;
}else{
/* Deny access to all others */
return MOSQ_ERR_AUTH;
}
}
int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count)
{
UNUSED(user_data);
UNUSED(opts);
UNUSED(opt_count);
mosq_pid = identifier;
return mosquitto_callback_register(mosq_pid, MOSQ_EVT_BASIC_AUTH, basic_auth_callback, NULL, NULL);
}
int mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count)
{
UNUSED(user_data);
UNUSED(opts);
UNUSED(opt_count);
return mosquitto_callback_unregister(mosq_pid, MOSQ_EVT_MESSAGE, basic_auth_callback, NULL);
}
+4
View File
@@ -0,0 +1,4 @@
listener 1883
plugin ./mosquitto_deny_protocol_version.so
password_file pwfile
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
../../apps/mosquitto_passwd/mosquitto_passwd -c -b pwfile username password
../../src/mosquitto -c test.conf -v
+94 -42
View File
@@ -718,10 +718,12 @@ static void client__remove_all_roles(struct dynsec__client *client)
int dynsec_clients__process_modify(struct dynsec__data *data, struct plugin_cmd *cmd, struct mosquitto *context)
{
char *username;
char *clientid;
char *password;
char *text_name, *text_description;
char *clientid = NULL;
char *password = NULL;
char *text_name = NULL, *text_description = NULL;
bool have_clientid = false, have_text_name = false, have_text_description = false, have_rolelist = false, have_password = false;
struct dynsec__client *client;
struct dynsec__group *group;
struct dynsec__rolelist *rolelist = NULL;
char *str;
int rc;
@@ -744,81 +746,87 @@ int dynsec_clients__process_modify(struct dynsec__data *data, struct plugin_cmd
return MOSQ_ERR_INVAL;
}
if(json_get_string(cmd->j_command, "clientid", &clientid, false) == MOSQ_ERR_SUCCESS){
if(clientid && strlen(clientid) > 0){
str = mosquitto_strdup(clientid);
if(str == NULL){
if(json_get_string(cmd->j_command, "clientid", &str, false) == MOSQ_ERR_SUCCESS){
have_clientid = true;
if(str && strlen(str) > 0){
clientid = mosquitto_strdup(str);
if(clientid == NULL){
plugin__command_reply(cmd, "Internal error");
return MOSQ_ERR_NOMEM;
rc = MOSQ_ERR_NOMEM;
goto error;
}
}else{
str = NULL;
clientid = NULL;
}
mosquitto_free(client->clientid);
client->clientid = str;
}
if(json_get_string(cmd->j_command, "password", &password, false) == MOSQ_ERR_SUCCESS){
if(strlen(password) > 0){
/* If password == "", we just ignore it */
rc = client__set_password(client, password);
if(rc != MOSQ_ERR_SUCCESS){
plugin__command_reply(cmd, "Internal error");
dynsec_kicklist__add(data, username);
return MOSQ_ERR_NOMEM;
}
have_password = true;
}
}
if(json_get_string(cmd->j_command, "textname", &text_name, false) == MOSQ_ERR_SUCCESS){
str = mosquitto_strdup(text_name);
if(str == NULL){
if(json_get_string(cmd->j_command, "textname", &str, false) == MOSQ_ERR_SUCCESS){
have_text_name = true;
text_name = mosquitto_strdup(str);
if(text_name == NULL){
plugin__command_reply(cmd, "Internal error");
dynsec_kicklist__add(data, username);
return MOSQ_ERR_NOMEM;
rc = MOSQ_ERR_NOMEM;
goto error;
}
mosquitto_free(client->text_name);
client->text_name = str;
}
if(json_get_string(cmd->j_command, "textdescription", &text_description, false) == MOSQ_ERR_SUCCESS){
str = mosquitto_strdup(text_description);
if(str == NULL){
if(json_get_string(cmd->j_command, "textdescription", &str, false) == MOSQ_ERR_SUCCESS){
have_text_description = true;
text_description = mosquitto_strdup(str);
if(text_description == NULL){
plugin__command_reply(cmd, "Internal error");
dynsec_kicklist__add(data, username);
return MOSQ_ERR_NOMEM;
rc = MOSQ_ERR_NOMEM;
goto error;
}
mosquitto_free(client->text_description);
client->text_description = str;
}
rc = dynsec_rolelist__load_from_json(data, cmd->j_command, &rolelist);
if(rc == MOSQ_ERR_SUCCESS){
client__remove_all_roles(client);
client__add_new_roles(client, rolelist);
dynsec_rolelist__cleanup(&rolelist);
have_rolelist = true;
}else if(rc == ERR_LIST_NOT_FOUND){
/* There was no list in the JSON, so no modification */
}else if(rc == MOSQ_ERR_NOT_FOUND){
plugin__command_reply(cmd, "Role not found");
dynsec_rolelist__cleanup(&rolelist);
dynsec_kicklist__add(data, username);
return MOSQ_ERR_INVAL;
rc = MOSQ_ERR_INVAL;
goto error;
}else{
if(rc == MOSQ_ERR_INVAL){
plugin__command_reply(cmd, "'roles' not an array or missing/invalid rolename");
}else{
plugin__command_reply(cmd, "Internal error");
}
dynsec_rolelist__cleanup(&rolelist);
dynsec_kicklist__add(data, username);
return MOSQ_ERR_INVAL;
rc = MOSQ_ERR_INVAL;
goto error;
}
j_groups = cJSON_GetObjectItem(cmd->j_command, "groups");
if(j_groups && cJSON_IsArray(j_groups)){
dynsec__remove_client_from_all_groups(data, username);
/* 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(data, jtmp->valuestring);
if(group == NULL){
plugin__command_reply(cmd, "'groups' contains an object with a 'groupname' that does not exist");
rc = MOSQ_ERR_INVAL;
goto error;
}
}else{
plugin__command_reply(cmd, "'groups' contains an object with an invalid 'groupname'");
rc = MOSQ_ERR_INVAL;
goto error;
}
}
}
dynsec__remove_client_from_all_groups(data, username);
cJSON_ArrayForEach(j_group, j_groups){
if(cJSON_IsObject(j_group)){
jtmp = cJSON_GetObjectItem(j_group, "groupname");
@@ -830,6 +838,44 @@ int dynsec_clients__process_modify(struct dynsec__data *data, struct plugin_cmd
}
}
if(have_password){
/* FIXME - This is the one call that will result in modification on internal error - note that groups have already been modified */
rc = client__set_password(client, password);
if(rc != MOSQ_ERR_SUCCESS){
plugin__command_reply(cmd, "Internal error");
mosquitto_kick_client_by_username(username, false);
/* If this fails we have the situation that the password is set as
* invalid, but the config isn't saved, so restarting the broker
* *now* will mean the client can log in again. This might be
* "good", but is inconsistent, so save the config to be
* consistent. */
dynsec__config_save(data);
rc = MOSQ_ERR_NOMEM;
goto error;
}
}
if(have_clientid){
mosquitto_free(client->clientid);
client->clientid = clientid;
}
if(have_text_name){
mosquitto_free(client->text_name);
client->text_name = text_name;
}
if(have_text_description){
mosquitto_free(client->text_description);
client->text_description = text_description;
}
if(have_rolelist){
client__remove_all_roles(client);
client__add_new_roles(client, rolelist);
dynsec_rolelist__cleanup(&rolelist);
}
dynsec__config_save(data);
plugin__command_reply(cmd, NULL);
@@ -841,6 +887,12 @@ int dynsec_clients__process_modify(struct dynsec__data *data, struct plugin_cmd
mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | modifyClient | username=%s",
admin_clientid, admin_username, username);
return MOSQ_ERR_SUCCESS;
error:
mosquitto_free(clientid);
mosquitto_free(text_name);
mosquitto_free(text_description);
dynsec_rolelist__cleanup(&rolelist);
return rc;
}
@@ -305,5 +305,6 @@ cJSON *dynsec_rolelist__all_to_json(struct dynsec__rolelist *base_rolelist);
int dynsec_kicklist__add(struct dynsec__data *data, const char *username);
void dynsec_kicklist__kick(struct dynsec__data *data);
int dynsec__tick_callback(int event, void *event_data, void *userdata);
void dynsec_kicklist__cleanup(struct dynsec__data *data);
#endif
+75 -24
View File
@@ -909,10 +909,12 @@ int dynsec_groups__process_remove_role(struct dynsec__data *data, struct plugin_
int dynsec_groups__process_modify(struct dynsec__data *data, struct plugin_cmd *cmd, struct mosquitto *context)
{
char *groupname;
char *text_name, *text_description;
struct dynsec__group *group;
char *groupname = NULL;
char *text_name = NULL, *text_description = NULL;
struct dynsec__client *client = NULL;
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;
@@ -934,52 +936,73 @@ int dynsec_groups__process_modify(struct dynsec__data *data, struct plugin_cmd *
return MOSQ_ERR_INVAL;
}
if(json_get_string(cmd->j_command, "textname", &text_name, false) == MOSQ_ERR_SUCCESS){
str = mosquitto_strdup(text_name);
if(str == NULL){
if(json_get_string(cmd->j_command, "textname", &str, false) == MOSQ_ERR_SUCCESS){
have_text_name = true;
text_name = mosquitto_strdup(str);
if(text_name == NULL){
plugin__command_reply(cmd, "Internal error");
return MOSQ_ERR_NOMEM;
rc = MOSQ_ERR_NOMEM;
goto error;
}
mosquitto_free(group->text_name);
group->text_name = str;
}
if(json_get_string(cmd->j_command, "textdescription", &text_description, false) == MOSQ_ERR_SUCCESS){
str = mosquitto_strdup(text_description);
if(str == NULL){
if(json_get_string(cmd->j_command, "textdescription", &str, false) == MOSQ_ERR_SUCCESS){
have_text_description = true;
text_description = mosquitto_strdup(str);
if(text_description == NULL){
plugin__command_reply(cmd, "Internal error");
return MOSQ_ERR_NOMEM;
rc = MOSQ_ERR_NOMEM;
goto error;
}
mosquitto_free(group->text_description);
group->text_description = str;
}
rc = dynsec_rolelist__load_from_json(data, cmd->j_command, &rolelist);
if(rc == MOSQ_ERR_SUCCESS){
dynsec_rolelist__cleanup(&group->rolelist);
group->rolelist = rolelist;
/* Apply changes below */
have_rolelist = true;
}else if(rc == ERR_LIST_NOT_FOUND){
/* There was no list in the JSON, so no modification */
rolelist = NULL;
}else if(rc == MOSQ_ERR_NOT_FOUND){
plugin__command_reply(cmd, "Role not found");
dynsec_rolelist__cleanup(&rolelist);
group__kick_all(data, group);
return MOSQ_ERR_INVAL;
rc = MOSQ_ERR_INVAL;
goto error;
}else{
if(rc == MOSQ_ERR_INVAL){
plugin__command_reply(cmd, "'roles' not an array or missing/invalid rolename");
}else{
plugin__command_reply(cmd, "Internal error");
}
dynsec_rolelist__cleanup(&rolelist);
group__kick_all(data, group);
return MOSQ_ERR_INVAL;
rc = MOSQ_ERR_INVAL;
goto error;
}
j_clients = cJSON_GetObjectItem(cmd->j_command, "clients");
if(j_clients && cJSON_IsArray(j_clients)){
/* 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(data, jtmp->valuestring);
if(client == NULL){
plugin__command_reply(cmd, "'clients' contains an object with a 'username' that does not exist");
rc = MOSQ_ERR_INVAL;
goto error;
}
}else{
plugin__command_reply(cmd, "'clients' contains an object with an invalid 'username'");
rc = MOSQ_ERR_INVAL;
goto error;
}
}
}
/* Kick all clients in the *current* group */
group__kick_all(data, group);
dynsec__remove_all_clients_from_group(group);
/* 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");
@@ -991,11 +1014,28 @@ int dynsec_groups__process_modify(struct dynsec__data *data, struct plugin_cmd *
}
}
/* Apply remaining changes to group, note that user changes are already applied */
if(have_text_name){
mosquitto_free(group->text_name);
group->text_name = text_name;
}
if(have_text_description){
mosquitto_free(group->text_description);
group->text_description = text_description;
}
if(have_rolelist){
dynsec_rolelist__cleanup(&group->rolelist);
group->rolelist = rolelist;
}
/* And save */
dynsec__config_save(data);
plugin__command_reply(cmd, NULL);
/* Enforce any changes */
/* Enforce any changes - kick any clients in the *new* group */
group__kick_all(data, group);
admin_clientid = mosquitto_client_id(context);
@@ -1004,6 +1044,17 @@ int dynsec_groups__process_modify(struct dynsec__data *data, struct plugin_cmd *
admin_clientid, admin_username, groupname);
return MOSQ_ERR_SUCCESS;
error:
mosquitto_free(text_name);
mosquitto_free(text_description);
dynsec_rolelist__cleanup(&rolelist);
admin_clientid = mosquitto_client_id(context);
admin_username = mosquitto_client_username(context);
mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | modifyGroup | groupname=%s",
admin_clientid, admin_username, groupname);
return rc;
}
+10
View File
@@ -64,3 +64,13 @@ void dynsec_kicklist__kick(struct dynsec__data *data)
free(kick);
}
}
void dynsec_kicklist__cleanup(struct dynsec__data *data)
{
struct dynsec__kicklist *kick, *tmp;
DL_FOREACH_SAFE(data->kicklist, kick, tmp){
DL_DELETE(data->kicklist, kick);
free(kick);
}
}
+53 -4
View File
@@ -24,6 +24,11 @@ Contributors:
#include <string.h>
#include <sys/stat.h>
#ifndef WIN32
# include <strings.h>
#endif
#include "json_help.h"
#include "mosquitto.h"
#include "mosquitto_broker.h"
#include "mosquitto_plugin.h"
@@ -39,6 +44,7 @@ static mosquitto_plugin_id_t *plg_id = NULL;
int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *options, int option_count)
{
int i;
int rc;
UNUSED(user_data);
@@ -66,12 +72,54 @@ int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, s
mosquitto_plugin_set_info(identifier, "dynamic-security", NULL);
dynsec__config_load(&dynsec_data);
mosquitto_callback_register(plg_id, MOSQ_EVT_CONTROL, dynsec_control_callback, "$CONTROL/dynamic-security/v1", &dynsec_data);
mosquitto_callback_register(plg_id, MOSQ_EVT_BASIC_AUTH, dynsec_auth__basic_auth_callback, NULL, &dynsec_data);
mosquitto_callback_register(plg_id, MOSQ_EVT_ACL_CHECK, dynsec__acl_check_callback, NULL, &dynsec_data);
mosquitto_callback_register(plg_id, MOSQ_EVT_TICK, dynsec__tick_callback, NULL, &dynsec_data);
rc = mosquitto_callback_register(plg_id, MOSQ_EVT_CONTROL, dynsec_control_callback, "$CONTROL/dynamic-security/v1", &dynsec_data);
if(rc == MOSQ_ERR_ALREADY_EXISTS){
mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Dynamic security plugin can currently only be loaded once.");
mosquitto_log_printf(MOSQ_LOG_ERR, "Note that this was previously incorrectly allowed but could cause problems with duplicate entries in the config.");
goto error;
}else if(rc == MOSQ_ERR_NOMEM){
mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Out of memory.");
goto error;
}else if(rc != MOSQ_ERR_SUCCESS){
goto error;
}
rc = mosquitto_callback_register(plg_id, MOSQ_EVT_BASIC_AUTH, dynsec_auth__basic_auth_callback, NULL, &dynsec_data);
if(rc == MOSQ_ERR_ALREADY_EXISTS){
mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Dynamic security plugin can only be loaded once.");
goto error;
}else if(rc == MOSQ_ERR_NOMEM){
mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Out of memory.");
goto error;
}else if(rc != MOSQ_ERR_SUCCESS){
goto error;
}
rc = mosquitto_callback_register(plg_id, MOSQ_EVT_ACL_CHECK, dynsec__acl_check_callback, NULL, &dynsec_data);
if(rc == MOSQ_ERR_ALREADY_EXISTS){
mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Dynamic security plugin can only be loaded once.");
goto error;
}else if(rc == MOSQ_ERR_NOMEM){
mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Out of memory.");
goto error;
}else if(rc != MOSQ_ERR_SUCCESS){
goto error;
}
rc = mosquitto_callback_register(plg_id, MOSQ_EVT_TICK, dynsec__tick_callback, NULL, &dynsec_data);
if(rc == MOSQ_ERR_NOMEM){
mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Out of memory.");
goto error;
}else if(rc != MOSQ_ERR_SUCCESS){
goto error;
}
return MOSQ_ERR_SUCCESS;
error:
mosquitto_free(dynsec_data.config_file);
dynsec_data.config_file = NULL;
return rc;
}
int mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *options, int option_count)
@@ -83,6 +131,7 @@ int mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *options, int
dynsec_groups__cleanup(&dynsec_data);
dynsec_clients__cleanup(&dynsec_data);
dynsec_roles__cleanup(&dynsec_data);
dynsec_kicklist__cleanup(&dynsec_data);
mosquitto_free(dynsec_data.config_file);
dynsec_data.config_file = NULL;
+4
View File
@@ -24,6 +24,10 @@ Contributors:
#include <uthash.h>
#include <utlist.h>
#ifndef WIN32
# include <strings.h>
#endif
#include "dynamic_security.h"
#include "json_help.h"
#include "mosquitto.h"
+2 -2
View File
@@ -11,9 +11,9 @@ ExecStart=/usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
ExecStartPre=/bin/mkdir -m 740 -p /var/log/mosquitto
ExecStartPre=/bin/chown mosquitto /var/log/mosquitto
ExecStartPre=/bin/chown mosquitto:mosquitto /var/log/mosquitto
ExecStartPre=/bin/mkdir -m 740 -p /run/mosquitto
ExecStartPre=/bin/chown mosquitto /run/mosquitto
ExecStartPre=/bin/chown mosquitto:mosquitto /run/mosquitto
[Install]
WantedBy=multi-user.target
+2 -2
View File
@@ -9,9 +9,9 @@ ExecStart=/usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
ExecStartPre=/bin/mkdir -m 740 -p /var/log/mosquitto
ExecStartPre=/bin/chown mosquitto /var/log/mosquitto
ExecStartPre=/bin/chown mosquitto:mosquitto /var/log/mosquitto
ExecStartPre=/bin/mkdir -m 740 -p /run/mosquitto
ExecStartPre=/bin/chown mosquitto /run/mosquitto
ExecStartPre=/bin/chown mosquitto:mosquitto /run/mosquitto
[Install]
WantedBy=multi-user.target

Some files were not shown because too many files have changed in this diff Show More