Dynsec: Kick clients in tick event rather than immediately

This means that PUBACK/PUBREC should be sent to clients that modify
their own roles.

Closes #2474. Thanks to Stefano Berlato.
pull/2485/head
Roger A. Light 4 years ago
parent aab5661868
commit 5279a424b1

@ -101,6 +101,8 @@ Plugins / plugin interface:
is present, including a set of default roles.
- Add `mosquitto_set_clientid()` to allow plugins to force a client id for a
client.
- The dynamic security plugin now only kicks clients at the start of the next
network loop, to give chance for PUBACK/PUBREC to be sent. Closes #2474.
Client library:
- Add MOSQ_OPT_DISABLE_SOCKETPAIR to allow the disabling of the socketpair

@ -32,12 +32,14 @@ if(CJSON_FOUND AND WITH_TLS)
grouplist.c
hash.c
../../common/json_help.c ../../common/json_help.h
kicklist.c
../../common/misc_mosq.c ../../common/misc_mosq.h
../../common/password_mosq.c ../../common/password_mosq.h
plugin.c
../common/plugin_common.c ../common/plugin_common.h
roles.c
rolelist.c
tick.c
)
target_include_directories(mosquitto_dynamic_security PRIVATE

@ -23,12 +23,14 @@ OBJS= \
grouplist.o \
hash.o \
json_help.o \
kicklist.o \
misc_mosq.o \
password_mosq.o \
plugin.o \
plugin_common.o \
roles.o \
rolelist.o
rolelist.o \
tick.o
ifeq ($(WITH_CJSON),yes)
ifeq ($(WITH_TLS),yes)
@ -85,6 +87,9 @@ hash.o : hash.c dynamic_security.h
json_help.o : ${R}/common/json_help.c ${R}/common/json_help.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@
kicklist.o : kicklist.c dynamic_security.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@
misc_mosq.o : ${R}/common/misc_mosq.c ${R}/common/misc_mosq.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@
@ -103,6 +108,9 @@ roles.o : roles.c dynamic_security.h
rolelist.o : rolelist.c dynamic_security.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@
tick.o : tick.c dynamic_security.h
${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@
reallyclean : clean
clean:
-rm -f *.o ${PLUGIN_NAME}.so *.gcda *.gcno

@ -62,12 +62,12 @@ static int dynsec_clientlist__cmp(void *a, void *b)
}
void dynsec_clientlist__kick_all(struct dynsec__clientlist *base_clientlist)
void dynsec_clientlist__kick_all(struct dynsec__data *data, struct dynsec__clientlist *base_clientlist)
{
struct dynsec__clientlist *clientlist, *clientlist_tmp;
HASH_ITER(hh, base_clientlist, clientlist, clientlist_tmp){
mosquitto_kick_client_by_username(clientlist->client->username, false);
dynsec_kicklist__add(data, clientlist->client->username);
}
}

@ -492,7 +492,7 @@ int dynsec_clients__process_delete(struct dynsec__data *data, struct plugin_cmd
plugin__command_reply(cmd, NULL);
/* Enforce any changes */
mosquitto_kick_client_by_username(username, false);
dynsec_kicklist__add(data, username);
admin_clientid = mosquitto_client_id(context);
admin_username = mosquitto_client_username(context);
@ -529,7 +529,7 @@ int dynsec_clients__process_disable(struct dynsec__data *data, struct plugin_cmd
client->disabled = true;
mosquitto_kick_client_by_username(username, false);
dynsec_kicklist__add(data, username);
dynsec__config_save(data);
plugin__command_reply(cmd, NULL);
@ -629,7 +629,7 @@ int dynsec_clients__process_set_id(struct dynsec__data *data, struct plugin_cmd
plugin__command_reply(cmd, NULL);
/* Enforce any changes */
mosquitto_kick_client_by_username(username, false);
dynsec_kicklist__add(data, username);
admin_clientid = mosquitto_client_id(context);
admin_username = mosquitto_client_username(context);
@ -689,7 +689,7 @@ int dynsec_clients__process_set_password(struct dynsec__data *data, struct plugi
plugin__command_reply(cmd, NULL);
/* Enforce any changes */
mosquitto_kick_client_by_username(username, false);
dynsec_kicklist__add(data, username);
admin_clientid = mosquitto_client_id(context);
admin_username = mosquitto_client_username(context);
@ -769,7 +769,7 @@ int dynsec_clients__process_modify(struct dynsec__data *data, struct plugin_cmd
rc = client__set_password(client, password);
if(rc != MOSQ_ERR_SUCCESS){
plugin__command_reply(cmd, "Internal error");
mosquitto_kick_client_by_username(username, false);
dynsec_kicklist__add(data, username);
return MOSQ_ERR_NOMEM;
}
}
@ -779,7 +779,7 @@ int dynsec_clients__process_modify(struct dynsec__data *data, struct plugin_cmd
str = mosquitto_strdup(text_name);
if(str == NULL){
plugin__command_reply(cmd, "Internal error");
mosquitto_kick_client_by_username(username, false);
dynsec_kicklist__add(data, username);
return MOSQ_ERR_NOMEM;
}
mosquitto_free(client->text_name);
@ -790,7 +790,7 @@ int dynsec_clients__process_modify(struct dynsec__data *data, struct plugin_cmd
str = mosquitto_strdup(text_description);
if(str == NULL){
plugin__command_reply(cmd, "Internal error");
mosquitto_kick_client_by_username(username, false);
dynsec_kicklist__add(data, username);
return MOSQ_ERR_NOMEM;
}
mosquitto_free(client->text_description);
@ -807,7 +807,7 @@ int dynsec_clients__process_modify(struct dynsec__data *data, struct plugin_cmd
}else if(rc == MOSQ_ERR_NOT_FOUND){
plugin__command_reply(cmd, "Role not found");
dynsec_rolelist__cleanup(&rolelist);
mosquitto_kick_client_by_username(username, false);
dynsec_kicklist__add(data, username);
return MOSQ_ERR_INVAL;
}else{
if(rc == MOSQ_ERR_INVAL){
@ -816,7 +816,7 @@ int dynsec_clients__process_modify(struct dynsec__data *data, struct plugin_cmd
plugin__command_reply(cmd, "Internal error");
}
dynsec_rolelist__cleanup(&rolelist);
mosquitto_kick_client_by_username(username, false);
dynsec_kicklist__add(data, username);
return MOSQ_ERR_INVAL;
}
@ -839,7 +839,7 @@ int dynsec_clients__process_modify(struct dynsec__data *data, struct plugin_cmd
plugin__command_reply(cmd, NULL);
/* Enforce any changes */
mosquitto_kick_client_by_username(username, false);
dynsec_kicklist__add(data, username);
admin_clientid = mosquitto_client_id(context);
admin_username = mosquitto_client_username(context);
@ -1122,7 +1122,7 @@ int dynsec_clients__process_add_role(struct dynsec__data *data, struct plugin_cm
plugin__command_reply(cmd, NULL);
/* Enforce any changes */
mosquitto_kick_client_by_username(username, false);
dynsec_kicklist__add(data, username);
admin_clientid = mosquitto_client_id(context);
admin_username = mosquitto_client_username(context);
@ -1176,7 +1176,7 @@ int dynsec_clients__process_remove_role(struct dynsec__data *data, struct plugin
plugin__command_reply(cmd, NULL);
/* Enforce any changes */
mosquitto_kick_client_by_username(username, false);
dynsec_kicklist__add(data, username);
admin_clientid = mosquitto_client_id(context);
admin_username = mosquitto_client_username(context);

@ -75,6 +75,11 @@ struct dynsec__rolelist{
int priority;
};
struct dynsec__kicklist{
struct dynsec__kicklist *next, *prev;
char username[];
};
struct dynsec__client{
UT_hash_handle hh;
struct mosquitto_pw pw;
@ -137,6 +142,7 @@ struct dynsec__data{
struct dynsec__group *groups;
struct dynsec__role *roles;
struct dynsec__group *anonymous_group;
struct dynsec__kicklist *kicklist;
struct dynsec__acl_default_access default_access;
};
@ -207,7 +213,7 @@ cJSON *dynsec_clientlist__all_to_json(struct dynsec__clientlist *base_clientlist
int dynsec_clientlist__add(struct dynsec__clientlist **base_clientlist, struct dynsec__client *client, int priority);
void dynsec_clientlist__cleanup(struct dynsec__clientlist **base_clientlist);
void dynsec_clientlist__remove(struct dynsec__clientlist **base_clientlist, struct dynsec__client *client);
void dynsec_clientlist__kick_all(struct dynsec__clientlist *base_clientlist);
void dynsec_clientlist__kick_all(struct dynsec__data *data, struct dynsec__clientlist *base_clientlist);
/* ################################################################
@ -280,4 +286,14 @@ int dynsec_rolelist__load_from_json(struct dynsec__data *data, cJSON *command, s
void dynsec_rolelist__cleanup(struct dynsec__rolelist **base_rolelist);
cJSON *dynsec_rolelist__all_to_json(struct dynsec__rolelist *base_rolelist);
/* ################################################################
* #
* # Kick List Functions
* #
* ################################################################ */
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);
#endif

@ -60,9 +60,9 @@ static cJSON *add_group_to_json(struct dynsec__group *group);
static void group__kick_all(struct dynsec__data *data, struct dynsec__group *group)
{
if(group == data->anonymous_group){
mosquitto_kick_client_by_username(NULL, false);
dynsec_kicklist__add(data, NULL);
}
dynsec_clientlist__kick_all(group->clientlist);
dynsec_clientlist__kick_all(data, group->clientlist);
}
@ -569,7 +569,7 @@ int dynsec_groups__process_add_client(struct dynsec__data *data, struct plugin_c
}
/* Enforce any changes */
mosquitto_kick_client_by_username(username, false);
dynsec_kicklist__add(data, username);
return rc;
}
@ -666,7 +666,7 @@ int dynsec_groups__process_remove_client(struct dynsec__data *data, struct plugi
}
/* Enforce any changes */
mosquitto_kick_client_by_username(username, false);
dynsec_kicklist__add(data, username);
return rc;
}
@ -1031,7 +1031,7 @@ int dynsec_groups__process_set_anonymous_group(struct dynsec__data *data, struct
plugin__command_reply(cmd, NULL);
/* Enforce any changes */
mosquitto_kick_client_by_username(NULL, false);
dynsec_kicklist__add(data, NULL);
admin_clientid = mosquitto_client_id(context);
admin_username = mosquitto_client_username(context);

@ -0,0 +1,66 @@
/*
Copyright (c) 2020-2021 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 BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include <stdio.h>
#include <utlist.h>
#include "mosquitto.h"
#include "mosquitto_broker.h"
#include "dynamic_security.h"
int dynsec_kicklist__add(struct dynsec__data *data, const char *username)
{
struct dynsec__kicklist *kick;
size_t slen;
if(username){
slen = strlen(username);
}else{
slen = 0;
}
kick = malloc(sizeof(struct dynsec__kicklist)+slen+1);
if(!kick){
return MOSQ_ERR_NOMEM;
}
if(username){
strcpy(kick->username, username);
}else{
kick->username[0] = '\0';
}
DL_APPEND(data->kicklist, kick);
return MOSQ_ERR_SUCCESS;
}
void dynsec_kicklist__kick(struct dynsec__data *data)
{
struct dynsec__kicklist *kick, *tmp;
DL_FOREACH_SAFE(data->kicklist, kick, tmp){
DL_DELETE(data->kicklist, kick);
if(strlen(kick->username)){
mosquitto_kick_client_by_username(kick->username, false);
}else{
mosquitto_kick_client_by_username(NULL, false);
}
free(kick);
}
}

@ -65,6 +65,7 @@ int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, s
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);
return MOSQ_ERR_SUCCESS;
}

@ -31,7 +31,7 @@ Contributors:
static cJSON *add_role_to_json(struct dynsec__role *role, bool verbose);
static void role__remove_all_clients(struct dynsec__role *role);
static void role__remove_all_clients(struct dynsec__data *data, struct dynsec__role *role);
/* ################################################################
* #
@ -108,13 +108,13 @@ static void role__kick_all(struct dynsec__data *data, struct dynsec__role *role)
{
struct dynsec__grouplist *grouplist, *grouplist_tmp = NULL;
dynsec_clientlist__kick_all(role->clientlist);
dynsec_clientlist__kick_all(data, role->clientlist);
HASH_ITER(hh, role->grouplist, grouplist, grouplist_tmp){
if(grouplist->group == data->anonymous_group){
mosquitto_kick_client_by_username(NULL, false);
dynsec_kicklist__add(data, NULL);
}
dynsec_clientlist__kick_all(grouplist->group->clientlist);
dynsec_clientlist__kick_all(data, grouplist->group->clientlist);
}
}
@ -437,12 +437,12 @@ error:
}
static void role__remove_all_clients(struct dynsec__role *role)
static void role__remove_all_clients(struct dynsec__data *data, struct dynsec__role *role)
{
struct dynsec__clientlist *clientlist, *clientlist_tmp = NULL;
HASH_ITER(hh, role->clientlist, clientlist, clientlist_tmp){
mosquitto_kick_client_by_username(clientlist->client->username, false);
dynsec_kicklist__add(data, clientlist->client->username);
dynsec_rolelist__client_remove(clientlist->client, role);
}
@ -454,9 +454,9 @@ static void role__remove_all_groups(struct dynsec__data *data, struct dynsec__ro
HASH_ITER(hh, role->grouplist, grouplist, grouplist_tmp){
if(grouplist->group == data->anonymous_group){
mosquitto_kick_client_by_username(NULL, false);
dynsec_kicklist__add(data, NULL);
}
dynsec_clientlist__kick_all(grouplist->group->clientlist);
dynsec_clientlist__kick_all(data, grouplist->group->clientlist);
dynsec_rolelist__group_remove(grouplist->group, role);
}
@ -479,7 +479,7 @@ int dynsec_roles__process_delete(struct dynsec__data *data, struct plugin_cmd *c
role = dynsec_roles__find(data, rolename);
if(role){
role__remove_all_clients(role);
role__remove_all_clients(data, role);
role__remove_all_groups(data, role);
role__free_item(data, role, true);
dynsec__config_save(data);

@ -0,0 +1,35 @@
/*
Copyright (c) 2020-2021 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 BSD-3-Clause
Contributors:
Roger Light - initial implementation and documentation.
*/
#include "config.h"
#include "mosquitto.h"
#include "mosquitto_broker.h"
#include "mosquitto_plugin.h"
#include "mqtt_protocol.h"
#include "dynamic_security.h"
int dynsec__tick_callback(int event, void *event_data, void *userdata)
{
UNUSED(event);
UNUSED(event_data);
dynsec_kicklist__kick(userdata);
return MOSQ_ERR_SUCCESS;
}
Loading…
Cancel
Save