Merge branch 'master' into debian

pull/684/head
Roger A. Light 8 years ago
commit d3bcb355f6

@ -11,7 +11,7 @@ project(mosquitto)
cmake_minimum_required(VERSION 2.8)
# Only for version 3 and up. cmake_policy(SET CMP0042 NEW)
set (VERSION 1.4.12)
set (VERSION 1.4.14)
if (WIN32)
execute_process(COMMAND cmd /c echo %DATE% %TIME% OUTPUT_VARIABLE TIMESTAMP

@ -1,3 +1,46 @@
Broker:
- Use constant time memcmp for password comparisons.
- Fix incorrect PSK key being used if it had leading zeroes.
Client library:
- Fix incorrect PSK key being used if it had leading zeroes.
1.4.14 - 20170710
=================
Broker:
- Fix regression from 1.4.13 where persistence data was not being saved.
1.4.13 - 20170627
=================
Security:
- Fix CVE-2017-9868. The persistence file was readable by all local users,
potentially allowing sensitive information to be leaked.
This can also be fixed administratively, by restricting access to the
directory in which the persistence file is stored.
Broker:
- Fix for poor websockets performance.
- Fix lazy bridges not timing out for idle_timeout. Closes #417.
- Fix problems with large retained messages over websockets. Closes #427.
- Set persistence file to only be readable by owner, except on Windows. Closes
#468.
- Fix CONNECT check for reserved=0, as per MQTT v3.1.1 check MQTT-3.1.2-3.
- When the broker stop, wills for any connected clients are now "sent". Closes
#477.
- Auth plugins can be configured to disable the check for +# in
usernames/client ids with the auth_plugin_deny_special_chars option.
Partially closes #462.
- Restrictions for CVE-2017-7650 have been relaxed - '/' is allowed in
usernames/client ids. Remainder of fix for #462.
Clients:
- Don't use / in auto-generated client ids.
1.4.12 - 20170528
=================

@ -727,14 +727,14 @@ int client_id_generate(struct mosq_config *cfg, const char *id_base)
hostname[0] = '\0';
gethostname(hostname, 256);
hostname[255] = '\0';
len = strlen(id_base) + strlen("/-") + 6 + strlen(hostname);
len = strlen(id_base) + strlen("|-") + 6 + strlen(hostname);
cfg->id = malloc(len);
if(!cfg->id){
if(!cfg->quiet) fprintf(stderr, "Error: Out of memory.\n");
mosquitto_lib_cleanup();
return 1;
}
snprintf(cfg->id, len, "%s/%d-%s", id_base, getpid(), hostname);
snprintf(cfg->id, len, "%s|%d-%s", id_base, getpid(), hostname);
if(strlen(cfg->id) > MOSQ_MQTT_ID_MAX_LENGTH){
/* Enforce maximum client id length of 23 characters */
cfg->id[MOSQ_MQTT_ID_MAX_LENGTH] = '\0';

@ -86,7 +86,7 @@ WITH_SOCKS:=yes
# Also bump lib/mosquitto.h, CMakeLists.txt,
# installer/mosquitto.nsi, installer/mosquitto-cygwin.nsi
VERSION=1.4.12
VERSION=1.4.14
TIMESTAMP:=$(shell date "+%F %T%z")
# Client library SO version. Bump if incompatible API/ABI changes are made.

@ -7,7 +7,7 @@
!define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
Name "mosquitto"
!define VERSION 1.4.12
!define VERSION 1.4.14
OutFile "mosquitto-${VERSION}-install-cygwin.exe"
InstallDir "$PROGRAMFILES\mosquitto"

@ -9,7 +9,7 @@
!define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
Name "mosquitto"
!define VERSION 1.4.12
!define VERSION 1.4.14
OutFile "mosquitto-${VERSION}-install-win32.exe"
InstallDir "$PROGRAMFILES\mosquitto"

@ -653,7 +653,7 @@ int mosquitto_tls_set(struct mosquitto *mosq, const char *cafile, const char *ca
if(!mosq || (!cafile && !capath) || (certfile && !keyfile) || (!certfile && keyfile)) return MOSQ_ERR_INVAL;
if(cafile){
fptr = _mosquitto_fopen(cafile, "rt");
fptr = _mosquitto_fopen(cafile, "rt", false);
if(fptr){
fclose(fptr);
}else{
@ -680,7 +680,7 @@ int mosquitto_tls_set(struct mosquitto *mosq, const char *cafile, const char *ca
}
if(certfile){
fptr = _mosquitto_fopen(certfile, "rt");
fptr = _mosquitto_fopen(certfile, "rt", false);
if(fptr){
fclose(fptr);
}else{
@ -704,7 +704,7 @@ int mosquitto_tls_set(struct mosquitto *mosq, const char *cafile, const char *ca
}
if(keyfile){
fptr = _mosquitto_fopen(keyfile, "rt");
fptr = _mosquitto_fopen(keyfile, "rt", false);
if(fptr){
fclose(fptr);
}else{
@ -971,9 +971,10 @@ int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets)
/* Fake write possible, to stimulate output write even though
* we didn't ask for it, because at that point the publish or
* other command wasn't present. */
FD_SET(mosq->sock, &writefds);
if(mosq->sock != INVALID_SOCKET)
FD_SET(mosq->sock, &writefds);
}
if(FD_ISSET(mosq->sock, &writefds)){
if(mosq->sock != INVALID_SOCKET && FD_ISSET(mosq->sock, &writefds)){
#ifdef WITH_TLS
if(mosq->want_connect){
rc = mosquitto__socket_connect_tls(mosq);

@ -45,7 +45,7 @@ extern "C" {
#define LIBMOSQUITTO_MAJOR 1
#define LIBMOSQUITTO_MINOR 4
#define LIBMOSQUITTO_REVISION 12
#define LIBMOSQUITTO_REVISION 14
/* 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)

@ -226,6 +226,7 @@ struct mosquitto {
struct libwebsocket *wsi;
# endif
# endif
bool ws_want_write;
#else
# ifdef WITH_SOCKS
char *socks5_host;

@ -464,6 +464,7 @@ int _mosquitto_try_connect(struct mosquitto *mosq, const char *host, uint16_t po
int mosquitto__socket_connect_tls(struct mosquitto *mosq)
{
int ret, err;
ERR_clear_error();
ret = SSL_connect(mosq->ssl);
if(ret != 1) {
err = SSL_get_error(mosq->ssl, ret);
@ -762,6 +763,7 @@ ssize_t _mosquitto_net_read(struct mosquitto *mosq, void *buf, size_t count)
errno = 0;
#ifdef WITH_TLS
if(mosq->ssl){
ERR_clear_error();
ret = SSL_read(mosq->ssl, buf, count);
if(ret <= 0){
err = SSL_get_error(mosq->ssl, ret);
@ -812,6 +814,7 @@ ssize_t _mosquitto_net_write(struct mosquitto *mosq, void *buf, size_t count)
#ifdef WITH_TLS
if(mosq->ssl){
mosq->want_write = false;
ERR_clear_error();
ret = SSL_write(mosq->ssl, buf, count);
if(ret < 0){
err = SSL_get_error(mosq->ssl, ret);

@ -18,7 +18,12 @@ Contributors:
#include <string.h>
#ifdef WIN32
#include <winsock2.h>
# include <winsock2.h>
# include <aclapi.h>
# include <io.h>
# include <lmcons.h>
#else
# include <sys/stat.h>
#endif
@ -322,23 +327,37 @@ int _mosquitto_hex2bin(const char *hex, unsigned char *bin, int bin_max_len)
{
BIGNUM *bn = NULL;
int len;
int leading_zero = 0;
int start = 0;
int i = 0;
/* Count the number of leading zero */
for(i=0; i<strlen(hex); i=i+2) {
if(strncmp(hex + i, "00", 2) == 0) {
leading_zero++;
/* output leading zero to bin */
bin[start++] = 0;
}else{
break;
}
}
if(BN_hex2bn(&bn, hex) == 0){
if(bn) BN_free(bn);
return 0;
}
if(BN_num_bytes(bn) > bin_max_len){
if(BN_num_bytes(bn) + leading_zero > bin_max_len){
BN_free(bn);
return 0;
}
len = BN_bn2bin(bn, bin);
len = BN_bn2bin(bn, bin + leading_zero);
BN_free(bn);
return len;
return len + leading_zero;
}
#endif
FILE *_mosquitto_fopen(const char *path, const char *mode)
FILE *_mosquitto_fopen(const char *path, const char *mode, bool restrict_read)
{
#ifdef WIN32
char buf[4096];
@ -347,10 +366,69 @@ FILE *_mosquitto_fopen(const char *path, const char *mode)
if(rc == 0 || rc > 4096){
return NULL;
}else{
return fopen(buf, mode);
if (restrict_read) {
HANDLE hfile;
SECURITY_ATTRIBUTES sec;
EXPLICIT_ACCESS ea;
PACL pacl = NULL;
char username[UNLEN + 1];
int ulen = UNLEN;
SECURITY_DESCRIPTOR sd;
GetUserName(username, &ulen);
if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) {
return NULL;
}
BuildExplicitAccessWithName(&ea, username, GENERIC_ALL, SET_ACCESS, NO_INHERITANCE);
if (SetEntriesInAcl(1, &ea, NULL, &pacl) != ERROR_SUCCESS) {
return NULL;
}
if (!SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE)) {
LocalFree(pacl);
return NULL;
}
sec.nLength = sizeof(SECURITY_ATTRIBUTES);
sec.bInheritHandle = FALSE;
sec.lpSecurityDescriptor = &sd;
hfile = CreateFile(buf, GENERIC_READ | GENERIC_WRITE, 0,
&sec,
CREATE_NEW,
FILE_ATTRIBUTE_NORMAL,
NULL);
LocalFree(pacl);
int fd = _open_osfhandle((intptr_t)hfile, 0);
if (fd < 0) {
return NULL;
}
FILE *fptr = _fdopen(fd, mode);
if (!fptr) {
_close(fd);
return NULL;
}
return fptr;
}else {
return fopen(buf, mode);
}
}
#else
return fopen(path, mode);
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);
}
#endif
}

@ -32,7 +32,7 @@ void _mosquitto_check_keepalive(struct mosquitto_db *db, struct mosquitto *mosq)
void _mosquitto_check_keepalive(struct mosquitto *mosq);
#endif
uint16_t _mosquitto_mid_generate(struct mosquitto *mosq);
FILE *_mosquitto_fopen(const char *path, const char *mode);
FILE *_mosquitto_fopen(const char *path, const char *mode, bool restrict_read);
#ifdef REAL_WITH_TLS_PSK
int _mosquitto_hex2bin(const char *hex, unsigned char *bin, int bin_max_len);

@ -8,6 +8,14 @@ clean :
reallyclean : clean
-rm -f *.orig
-rm -f libmosquitto.3
-rm -f mosquitto.8
-rm -f mosquitto.conf.5
-rm -f mosquitto_passwd.1
-rm -f mosquitto_pub.1
-rm -f mosquitto_sub.1
-rm -f mosquitto-tls.7
-rm -f mqtt.7
dist : mosquitto.8 mosquitto-tls.7 mosquitto.conf.5 mosquitto_passwd.1 mosquitto_pub.1 mosquitto_sub.1 mqtt.7 libmosquitto.3

@ -473,7 +473,8 @@
<refsect1>
<title>Bugs</title>
<para><command>mosquitto</command> bug information can be found at <uri type="webpage">https://bugs.eclipse.org/bugs/describecomponents.cgi?product=Mosquitto</uri></para>
<para><command>mosquitto</command> bug information can be found at
<ulink url="https://github.com/eclipse/mosquitto/issues"/></para>
</refsect1>
<refsect1>
@ -499,7 +500,7 @@
</member>
<member>
<citerefentry>
<refentrytitle><link xlink:href="http://www.linuxmanpages.com/man5/hosts_access.5.html">hosts_access</link></refentrytitle>
<refentrytitle><link xlink:href="https://linux.die.net/man/5/hosts_access">hosts_access</link></refentrytitle>
<manvolnum>5</manvolnum>
</citerefentry>
</member>

@ -202,6 +202,31 @@
<para>Not currently reloaded on reload signal.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>auth_plugin_deny_special_chars</option> [ true | false ]</term>
<listitem>
<para>If <replaceable>true</replaceable> then before an ACL
check is made, the username/client id of the client
needing the check is searched for the presence of
either a '+' or '#' character. If either of these
characters is found in either the username or client
id, then the ACL check is denied before it is sent to
the plugin.</para>
<para>This check prevents the case where a malicious user
could circumvent an ACL check by using one of these
characters as their username or client id. This is the
same issue as was reported with mosquitto itself as
CVE-2017-7650.</para>
<para>If you are entirely sure that the plugin you are
using is not vulnerable to this attack (i.e. if you
never use usernames or client ids in topics) then you
can disable this extra check and hence have all ACL
checks delivered to your plugin by setting this option
to <replaceable>false</replaceable>.</para>
<para>Defaults to <replaceable>true</replaceable>.</para>
<para>Not currently reloaded on reload signal.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>autosave_interval</option> <replaceable>seconds</replaceable></term>
<listitem>
@ -1396,8 +1421,8 @@ topic clients/total in 0 test/mosquitto/org $SYS/broker/
<refsect1>
<title>Bugs</title>
<para><command>mosquitto</command> bug information can be found at <uri
type="webpage">https://bugs.eclipse.org/bugs/describecomponents.cgi?product=Mosquitto</uri></para>
<para><command>mosquitto</command> bug information can be found at
<ulink url="https://github.com/eclipse/mosquitto/issues"/></para>
</refsect1>
<refsect1>

@ -123,8 +123,8 @@
<refsect1>
<title>Bugs</title>
<para><command>mosquitto_passwd</command> bug information can be found at
<uri type="webpage">https://bugs.eclipse.org/bugs/describecomponents.cgi?product=Mosquitto</uri></para>
<para><command>mosquitto</command> bug information can be found at
<ulink url="https://github.com/eclipse/mosquitto/issues"/></para>
</refsect1>
<refsect1>

@ -475,7 +475,8 @@
<refsect1>
<title>Bugs</title>
<para><command>mosquitto_pub</command> bug information can be found at <uri type="webpage">https://bugs.eclipse.org/bugs/describecomponents.cgi?product=Mosquitto</uri></para>
<para><command>mosquitto</command> bug information can be found at
<ulink url="https://github.com/eclipse/mosquitto/issues"/></para>
</refsect1>
<refsect1>

@ -508,8 +508,8 @@
<refsect1>
<title>Bugs</title>
<para><command>mosquitto_sub</command> bug information can be found at
<uri type="webpage">https://bugs.eclipse.org/bugs/describecomponents.cgi?product=Mosquitto</uri></para>
<para><command>mosquitto</command> bug information can be found at
<ulink url="https://github.com/eclipse/mosquitto/issues"/></para>
</refsect1>
<refsect1>

@ -521,6 +521,22 @@
# "Authentication and topic access plugin options" section below.
#auth_plugin
# If auth_plugin_deny_special_chars is true, the default, then before an ACL
# check is made, the username/client id of the client needing the check is
# searched for the presence of either a '+' or '#' character. If either of
# these characters is found in either the username or client id, then the ACL
# check is denied before it is sent to the plugin.o
#
# This check prevents the case where a malicious user could circumvent an ACL
# check by using one of these characters as their username or client id. This
# is the same issue as was reported with mosquitto itself as CVE-2017-7650.
#
# If you are entirely sure that the plugin you are using is not vulnerable to
# this attack (i.e. if you never use usernames or client ids in topics) then
# you can disable this extra check and have all ACL checks delivered to your
# plugin by setting auth_plugin_deny_special_chars to false.
#auth_plugin_deny_special_chars true
# -----------------------------------------------------------------
# Default authentication and topic access control
# -----------------------------------------------------------------

@ -2,7 +2,7 @@
MAJOR=1
MINOR=4
REVISION=12
REVISION=14
sed -i "s/^VERSION=.*/VERSION=${MAJOR}.${MINOR}.${REVISION}/" config.mk

@ -204,6 +204,7 @@ void mqtt3_config_init(struct mqtt3_config *config)
config->bridge_count = 0;
#endif
config->auth_plugin = NULL;
config->auth_plugin_deny_special_chars = true;
config->verbose = false;
config->message_size_limit = 0;
}
@ -669,6 +670,9 @@ int _config_read_file_core(struct mqtt3_config *config, bool reload, const char
}else if(!strcmp(token, "auth_plugin")){
if(reload) continue; // Auth plugin not currently valid for reloading.
if(_conf_parse_string(&token, "auth_plugin", &config->auth_plugin, saveptr)) return MOSQ_ERR_INVAL;
}else if(!strcmp(token, "auth_plugin_deny_special_chars")){
if(reload) continue; // Auth plugin not currently valid for reloading.
if(_conf_parse_bool(&token, "auth_plugin_deny_special_chars", &config->auth_plugin_deny_special_chars, saveptr)) return MOSQ_ERR_INVAL;
}else if(!strcmp(token, "auto_id_prefix")){
if(_conf_parse_string(&token, "auto_id_prefix", &config->auto_id_prefix, saveptr)) return MOSQ_ERR_INVAL;
if(config->auto_id_prefix){
@ -1762,7 +1766,7 @@ int _config_read_file(struct mqtt3_config *config, bool reload, const char *file
int rc;
FILE *fptr = NULL;
fptr = _mosquitto_fopen(file, "rt");
fptr = _mosquitto_fopen(file, "rt", false);
if(!fptr){
_mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open config file %s\n", file);
return 1;

@ -143,6 +143,8 @@ void mqtt3_context_cleanup(struct mosquitto_db *db, struct mosquitto *context, b
context->address = NULL;
}
mqtt3_context_send_will(db, context);
if(context->id){
assert(db); /* db can only be NULL here if the client hasn't sent a
CONNECT and hence wouldn't have an id. */
@ -163,12 +165,6 @@ void mqtt3_context_cleanup(struct mosquitto_db *db, struct mosquitto *context, b
context->out_packet = context->out_packet->next;
_mosquitto_free(packet);
}
if(context->will){
if(context->will->topic) _mosquitto_free(context->will->topic);
if(context->will->payload) _mosquitto_free(context->will->payload);
_mosquitto_free(context->will);
context->will = NULL;
}
if(do_free || context->clean_session){
msg = context->msgs;
while(msg){
@ -185,7 +181,8 @@ void mqtt3_context_cleanup(struct mosquitto_db *db, struct mosquitto *context, b
}
}
void mqtt3_context_disconnect(struct mosquitto_db *db, struct mosquitto *ctxt)
void mqtt3_context_send_will(struct mosquitto_db *db, struct mosquitto *ctxt)
{
if(ctxt->state != mosq_cs_disconnecting && ctxt->will){
if(mosquitto_acl_check(db, ctxt, ctxt->will->topic, MOSQ_ACL_WRITE) == MOSQ_ERR_SUCCESS){
@ -199,6 +196,13 @@ void mqtt3_context_disconnect(struct mosquitto_db *db, struct mosquitto *ctxt)
_mosquitto_free(ctxt->will);
ctxt->will = NULL;
}
}
void mqtt3_context_disconnect(struct mosquitto_db *db, struct mosquitto *ctxt)
{
mqtt3_context_send_will(db, ctxt);
ctxt->disconnect_t = time(NULL);
_mosquitto_socket_close(db, ctxt);
}

@ -69,7 +69,7 @@ int mqtt3_log_init(struct mqtt3_config *config)
if(drop_privileges(config, true)){
return 1;
}
config->log_fptr = _mosquitto_fopen(config->log_file, "at");
config->log_fptr = _mosquitto_fopen(config->log_file, "at", true);
if(!config->log_fptr){
log_destinations = MQTT3_LOG_STDERR;
log_priorities = MOSQ_LOG_ERR;

@ -21,6 +21,7 @@ Contributors:
#include <assert.h>
#ifndef WIN32
#include <poll.h>
#include <unistd.h>
#else
#include <process.h>
#include <winsock2.h>
@ -106,13 +107,12 @@ int mosquitto_main_loop(struct mosquitto_db *db, mosq_sock_t *listensock, int li
#endif
int i;
struct pollfd *pollfds = NULL;
int pollfd_count = 0;
int pollfd_index;
int pollfd_max;
#ifdef WITH_BRIDGE
mosq_sock_t bridge_sock;
int rc;
#endif
int context_count;
time_t expiration_check_time = 0;
time_t last_timeout_check = 0;
char *id;
@ -120,8 +120,22 @@ int mosquitto_main_loop(struct mosquitto_db *db, mosq_sock_t *listensock, int li
#ifndef WIN32
sigemptyset(&sigblock);
sigaddset(&sigblock, SIGINT);
sigaddset(&sigblock, SIGTERM);
sigaddset(&sigblock, SIGHUP);
#endif
#ifdef WIN32
pollfd_max = _getmaxstdio();
#else
pollfd_max = sysconf(_SC_OPEN_MAX);
#endif
pollfds = _mosquitto_malloc(sizeof(struct pollfd)*pollfd_max);
if(!pollfds){
_mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
if(db->config->persistent_client_expiration > 0){
expiration_check_time = time(NULL) + 3600;
}
@ -134,21 +148,7 @@ int mosquitto_main_loop(struct mosquitto_db *db, mosq_sock_t *listensock, int li
}
#endif
context_count = HASH_CNT(hh_sock, db->contexts_by_sock);
#ifdef WITH_BRIDGE
context_count += db->bridge_count;
#endif
if(listensock_count + context_count > pollfd_count || !pollfds){
pollfd_count = listensock_count + context_count;
pollfds = _mosquitto_realloc(pollfds, sizeof(struct pollfd)*pollfd_count);
if(!pollfds){
_mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
}
memset(pollfds, -1, sizeof(struct pollfd)*pollfd_count);
memset(pollfds, -1, sizeof(struct pollfd)*pollfd_max);
pollfd_index = 0;
for(i=0; i<listensock_count; i++){
@ -196,8 +196,9 @@ int mosquitto_main_loop(struct mosquitto_db *db, mosq_sock_t *listensock, int li
pollfds[pollfd_index].fd = context->sock;
pollfds[pollfd_index].events = POLLIN;
pollfds[pollfd_index].revents = 0;
if(context->current_out_packet || context->state == mosq_cs_connect_pending){
if(context->current_out_packet || context->state == mosq_cs_connect_pending || context->ws_want_write){
pollfds[pollfd_index].events |= POLLOUT;
context->ws_want_write = false;
}
context->pollfd_index = pollfd_index;
pollfd_index++;
@ -436,7 +437,11 @@ void do_disconnect(struct mosquitto_db *db, struct mosquitto *context)
if(context->wsi){
libwebsocket_callback_on_writable(context->ws_context, context->wsi);
}
context->sock = INVALID_SOCKET;
if(context->sock != INVALID_SOCKET){
HASH_DELETE(hh_sock, db->contexts_by_sock, context);
context->sock = INVALID_SOCKET;
context->pollfd_index = -1;
}
}else
#endif
{
@ -482,6 +487,18 @@ static void loop_handle_reads_writes(struct mosquitto_db *db, struct pollfd *pol
}
assert(pollfds[context->pollfd_index].fd == context->sock);
#ifdef WITH_WEBSOCKETS
if(context->wsi){
struct lws_pollfd wspoll;
wspoll.fd = pollfds[context->pollfd_index].fd;
wspoll.events = pollfds[context->pollfd_index].events;
wspoll.revents = pollfds[context->pollfd_index].revents;
lws_service_fd(lws_get_context(context->wsi), &wspoll);
continue;
}
#endif
#ifdef WITH_TLS
if(pollfds[context->pollfd_index].revents & POLLOUT ||
context->want_write ||
@ -511,6 +528,12 @@ static void loop_handle_reads_writes(struct mosquitto_db *db, struct pollfd *pol
if(context->pollfd_index < 0){
continue;
}
#ifdef WITH_WEBSOCKETS
if(context->wsi){
// Websocket are already handled above
continue;
}
#endif
#ifdef WITH_TLS
if(pollfds[context->pollfd_index].revents & POLLIN ||
@ -525,7 +548,7 @@ static void loop_handle_reads_writes(struct mosquitto_db *db, struct pollfd *pol
}
}while(SSL_DATA_PENDING(context));
}
if(pollfds[context->pollfd_index].revents & (POLLERR | POLLNVAL | POLLHUP)){
if(context->pollfd_index >= 0 && pollfds[context->pollfd_index].revents & (POLLERR | POLLNVAL | POLLHUP)){
do_disconnect(db, context);
continue;
}

@ -269,7 +269,7 @@ int main(int argc, char *argv[])
}
if(config.daemon && config.pid_file){
pid = _mosquitto_fopen(config.pid_file, "wt");
pid = _mosquitto_fopen(config.pid_file, "wt", false);
if(pid){
fprintf(pid, "%d", getpid());
fclose(pid);
@ -387,12 +387,6 @@ int main(int argc, char *argv[])
_mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "mosquitto version %s terminating", VERSION);
mqtt3_log_close(&config);
#ifdef WITH_PERSISTENCE
if(config.persistence){
mqtt3_db_backup(&int_db, true);
}
#endif
#ifdef WITH_WEBSOCKETS
for(i=0; i<int_db.config->listener_count; i++){
if(int_db.config->listeners[i].ws_context){
@ -404,6 +398,16 @@ int main(int argc, char *argv[])
}
#endif
HASH_ITER(hh_id, int_db.contexts_by_id, ctxt, ctxt_tmp){
mqtt3_context_send_will(&int_db, ctxt);
}
#ifdef WITH_PERSISTENCE
if(config.persistence){
mqtt3_db_backup(&int_db, true);
}
#endif
HASH_ITER(hh_id, int_db.contexts_by_id, ctxt, ctxt_tmp){
#ifdef WITH_WEBSOCKETS
if(!ctxt->wsi){

@ -106,6 +106,7 @@ struct mqtt3_config {
bool allow_anonymous;
bool allow_duplicate_messages;
bool allow_zero_length_clientid;
bool auth_plugin_deny_special_chars;
char *auto_id_prefix;
int auto_id_prefix_len;
int autosave_interval;
@ -452,6 +453,7 @@ void mqtt3_context_cleanup(struct mosquitto_db *db, struct mosquitto *context, b
void mqtt3_context_disconnect(struct mosquitto_db *db, struct mosquitto *context);
void mosquitto__add_context_to_disused(struct mosquitto_db *db, struct mosquitto *context);
void mosquitto__free_disused_contexts(struct mosquitto_db *db);
void mqtt3_context_send_will(struct mosquitto_db *db, struct mosquitto *context);
/* ============================================================
* Logging functions

@ -165,6 +165,7 @@ int mqtt3_socket_accept(struct mosquitto_db *db, mosq_sock_t listensock)
new_context->want_write = true;
bio = BIO_new_socket(new_sock, BIO_NOCLOSE);
SSL_set_bio(new_context->ssl, bio, bio);
ERR_clear_error();
rc = SSL_accept(new_context->ssl);
if(rc != 1){
rc = SSL_get_error(new_context->ssl, rc);

@ -404,7 +404,7 @@ int mqtt3_db_backup(struct mosquitto_db *db, bool shutdown)
}
#endif
db_fptr = _mosquitto_fopen(outfile, "wb");
db_fptr = _mosquitto_fopen(outfile, "wb", true);
if(db_fptr == NULL){
_mosquitto_log_printf(NULL, MOSQ_LOG_INFO, "Error saving in-memory database, unable to open %s for writing.", outfile);
goto error;
@ -821,7 +821,7 @@ int mqtt3_db_restore(struct mosquitto_db *db)
db->msg_store_load = NULL;
fptr = _mosquitto_fopen(db->config->persistence_filepath, "rb");
fptr = _mosquitto_fopen(db->config->persistence_filepath, "rb", false);
if(fptr == NULL) return MOSQ_ERR_SUCCESS;
rlen = fread(&header, 1, 15, fptr);
if(rlen == 0){

@ -171,6 +171,13 @@ int mqtt3_handle_connect(struct mosquitto_db *db, struct mosquitto *context)
rc = 1;
goto handle_connect_error;
}
if(context->protocol == mosq_p_mqtt311){
if((connect_flags & 0x01) != 0x00){
rc = MOSQ_ERR_PROTOCOL;
goto handle_connect_error;
}
}
clean_session = (connect_flags & 0x02) >> 1;
will = connect_flags & 0x04;
will_qos = (connect_flags & 0x18) >> 3;

@ -234,19 +234,21 @@ int mosquitto_acl_check(struct mosquitto_db *db, struct mosquitto *context, cons
username = context->username;
}
/* Check whether the client id or username contains a +, # or / and if
* so deny access.
*
* Do this check for every message regardless, we have to protect the
* plugins against possible pattern based attacks.
*/
if(username && strpbrk(username, "+#/")){
_mosquitto_log_printf(NULL, MOSQ_LOG_NOTICE, "ACL denying access to client with dangerous username \"%s\"", username);
return MOSQ_ERR_ACL_DENIED;
}
if(context->id && strpbrk(context->id, "+#/")){
_mosquitto_log_printf(NULL, MOSQ_LOG_NOTICE, "ACL denying access to client with dangerous client id \"%s\"", context->id);
return MOSQ_ERR_ACL_DENIED;
if(db->config->auth_plugin_deny_special_chars == true){
/* Check whether the client id or username contains a + or # and if
* so deny access.
*
* Do this check for every message regardless, we have to protect the
* plugins against possible pattern based attacks.
*/
if(username && strpbrk(username, "+#")){
_mosquitto_log_printf(NULL, MOSQ_LOG_NOTICE, "ACL denying access to client with dangerous username \"%s\"", username);
return MOSQ_ERR_ACL_DENIED;
}
if(context->id && strpbrk(context->id, "+#")){
_mosquitto_log_printf(NULL, MOSQ_LOG_NOTICE, "ACL denying access to client with dangerous client id \"%s\"", context->id);
return MOSQ_ERR_ACL_DENIED;
}
}
return db->auth_plugin.acl_check(db->auth_plugin.user_data, context->id, username, topic, access);
}

@ -33,6 +33,9 @@ static int _pw_digest(const char *password, const unsigned char *salt, unsigned
static int _base64_decode(char *in, unsigned char **decoded, unsigned int *decoded_len);
#endif
static int mosquitto__memcmp_const(const void *ptr1, const void *b, size_t len);
int mosquitto_security_init_default(struct mosquitto_db *db, bool reload)
{
int rc;
@ -264,18 +267,18 @@ int mosquitto_acl_check_default(struct mosquitto_db *db, struct mosquitto *conte
if(acl_root){
/* We are using pattern based acls. Check whether the username or
* client id contains a +, # or / and if so deny access.
* client id contains a + or # and if so deny access.
*
* Without this, a malicious client may configure its username/client
* id to bypass ACL checks (or have a username/client id that cannot
* publish or receive messages to its own place in the hierarchy).
*/
if(context->username && strpbrk(context->username, "+#/")){
if(context->username && strpbrk(context->username, "+#")){
_mosquitto_log_printf(NULL, MOSQ_LOG_NOTICE, "ACL denying access to client with dangerous username \"%s\"", context->username);
return MOSQ_ERR_ACL_DENIED;
}
if(context->id && strpbrk(context->id, "+#/")){
if(context->id && strpbrk(context->id, "+#")){
_mosquitto_log_printf(NULL, MOSQ_LOG_NOTICE, "ACL denying access to client with dangerous client id \"%s\"", context->id);
return MOSQ_ERR_ACL_DENIED;
}
@ -352,7 +355,7 @@ static int _aclfile_parse(struct mosquitto_db *db)
if(!db || !db->config) return MOSQ_ERR_INVAL;
if(!db->config->acl_file) return MOSQ_ERR_SUCCESS;
aclfile = _mosquitto_fopen(db->config->acl_file, "rt");
aclfile = _mosquitto_fopen(db->config->acl_file, "rt", false);
if(!aclfile){
_mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open acl_file \"%s\".", db->config->acl_file);
return 1;
@ -511,7 +514,7 @@ static int _pwfile_parse(const char *file, struct _mosquitto_unpwd **root)
int len;
char *saveptr = NULL;
pwfile = _mosquitto_fopen(file, "rt");
pwfile = _mosquitto_fopen(file, "rt", false);
if(!pwfile){
_mosquitto_log_printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open pwfile \"%s\".", file);
return 1;
@ -650,6 +653,23 @@ static int _psk_file_parse(struct mosquitto_db *db)
return MOSQ_ERR_SUCCESS;
}
static int mosquitto__memcmp_const(const void *a, const void *b, size_t len)
{
int i;
int rc = 0;
if(!a || !b) return 1;
for(i=0; i<len; i++){
if( ((char *)a)[i] != ((char *)b)[i] ){
rc = 1;
}
}
return rc;
}
int mosquitto_unpwd_check_default(struct mosquitto_db *db, const char *username, const char *password)
{
struct _mosquitto_unpwd *u, *tmp;
@ -670,7 +690,7 @@ int mosquitto_unpwd_check_default(struct mosquitto_db *db, const char *username,
#ifdef WITH_TLS
rc = _pw_digest(password, u->salt, u->salt_len, hash, &hash_len);
if(rc == MOSQ_ERR_SUCCESS){
if(hash_len == u->password_len && !memcmp(u->password, hash, hash_len)){
if(hash_len == u->password_len && !mosquitto__memcmp_const(u->password, hash, hash_len)){
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_AUTH;

@ -218,6 +218,8 @@ static int callback_mqtt(struct libwebsocket_context *context,
u->mosq = NULL;
return -1;
}
mosq->sock = libwebsocket_get_socket_fd(wsi);
HASH_ADD(hh_sock, db->contexts_by_sock, sock, sizeof(mosq->sock), mosq);
break;
case LWS_CALLBACK_CLOSED:
@ -226,6 +228,11 @@ static int callback_mqtt(struct libwebsocket_context *context,
}
mosq = u->mosq;
if(mosq){
if(mosq->sock > 0){
HASH_DELETE(hh_sock, db->contexts_by_sock, mosq);
mosq->sock = INVALID_SOCKET;
mosq->pollfd_index = -1;
}
mosq->wsi = NULL;
do_disconnect(db, mosq);
}
@ -250,7 +257,7 @@ static int callback_mqtt(struct libwebsocket_context *context,
}
}
while(mosq->current_out_packet && !lws_send_pipe_choked(mosq->wsi)){
if(mosq->current_out_packet && !lws_send_pipe_choked(mosq->wsi)){
packet = mosq->current_out_packet;
if(packet->pos == 0 && packet->to_process == packet->packet_length){
@ -290,10 +297,6 @@ static int callback_mqtt(struct libwebsocket_context *context,
_mosquitto_free(packet);
mosq->next_msg_out = mosquitto_time() + mosq->keepalive;
if(mosq->current_out_packet){
libwebsocket_callback_on_writable(mosq->ws_context, mosq->wsi);
}
}
if(mosq->current_out_packet){
libwebsocket_callback_on_writable(mosq->ws_context, mosq->wsi);
@ -412,6 +415,9 @@ static int callback_http(struct libwebsocket_context *context,
char *filename, *filename_canonical;
unsigned char buf[4096];
struct stat filestat;
struct mosquitto_db *db = &int_db;
struct mosquitto *mosq;
struct lws_pollargs *pollargs = (struct lws_pollargs *)in;
/* FIXME - ssl cert verification is done here. */
@ -583,6 +589,15 @@ static int callback_http(struct libwebsocket_context *context,
break;
#endif
case LWS_CALLBACK_ADD_POLL_FD:
case LWS_CALLBACK_DEL_POLL_FD:
case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
HASH_FIND(hh_sock, db->contexts_by_sock, &pollargs->fd, sizeof(pollargs->fd), mosq);
if(mosq && (pollargs->events & POLLOUT)){
mosq->ws_want_write = true;
}
break;
default:
return 0;
}

@ -0,0 +1,33 @@
#!/usr/bin/env python
# Test whether a CONNECT with reserved set to 1 results in a disconnect. MQTT-3.1.2-3
import inspect, os, sys
# From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder
cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],"..")))
if cmd_subfolder not in sys.path:
sys.path.insert(0, cmd_subfolder)
import mosq_test
rc = 1
keepalive = 10
connect_packet = mosq_test.gen_connect("connect-invalid-test", keepalive=keepalive, connect_reserved=True, proto_ver=4)
cmd = ['../../src/mosquitto', '-p', '1888']
broker = mosq_test.start_broker(filename=os.path.basename(__file__), cmd=cmd)
try:
sock = mosq_test.do_client_connect(connect_packet, "")
sock.close()
rc = 0
finally:
broker.terminate()
broker.wait()
if rc:
(stdo, stde) = broker.communicate()
print(stde)
exit(rc)

@ -20,6 +20,7 @@ test : test-compile 01 02 03 04 05 06 07 08 09 10
./01-connect-invalid-id-0.py
./01-connect-invalid-id-0-311.py
./01-connect-invalid-id-missing.py
./01-connect-invalid-reserved.py
./01-connect-anon-denied.py
./01-connect-uname-no-password-denied.py
./01-connect-uname-password-denied.py

@ -91,6 +91,19 @@ def remaining_length(packet):
return (packet, rl)
def to_hex_string(packet):
if len(packet) == 0:
return ""
s = ""
while len(packet) > 0:
packet0 = struct.unpack("!B", packet[0])
s = s+hex(packet0[0]) + " "
packet = packet[1:]
return s
def to_string(packet):
if len(packet) == 0:
return ""
@ -150,6 +163,9 @@ def to_string(packet):
(password, packet) = struct.unpack(pack_format, packet)
s = s+", password="+password
if flags&1:
s = s+", reserved=1"
return s
elif cmd == 0x20:
# CONNACK
@ -250,7 +266,7 @@ def to_string(packet):
# Reserved
return "0xF0"
def gen_connect(client_id, clean_session=True, keepalive=60, username=None, password=None, will_topic=None, will_qos=0, will_retain=False, will_payload="", proto_ver=3):
def gen_connect(client_id, clean_session=True, keepalive=60, username=None, password=None, will_topic=None, will_qos=0, will_retain=False, will_payload="", proto_ver=3, connect_reserved=False):
if (proto_ver&0x7F) == 3 or proto_ver == 0:
remaining_length = 12
elif (proto_ver&0x7F) == 4:
@ -262,6 +278,10 @@ def gen_connect(client_id, clean_session=True, keepalive=60, username=None, pass
remaining_length = remaining_length + 2+len(client_id)
connect_flags = 0
if connect_reserved:
connect_flags = connect_flags | 0x01
if clean_session:
connect_flags = connect_flags | 0x02

Loading…
Cancel
Save