Dynsec: Kick affected clients on role change.

This commit is contained in:
Roger A. Light 2020-11-03 18:16:44 +00:00
parent 9785896eea
commit 3486f0e546
4 changed files with 206 additions and 56 deletions

View File

@ -49,6 +49,14 @@ static struct dynsec__client *local_clients = NULL;
* #
* ################################################################ */
int dynsec_clientlist__cmp(void *a, void *b)
{
struct dynsec__clientlist *clientlist_a = a;
struct dynsec__clientlist *clientlist_b = b;
return strcmp(clientlist_a->username, clientlist_b->username);
}
static int client_cmp(void *a, void *b)
{
struct dynsec__client *client_a = a;
@ -243,7 +251,7 @@ int dynsec_clients__config_load(cJSON *tree)
if(jtmp && cJSON_IsString(jtmp)){
json_get_int(j_role, "priority", &priority, true, -1);
role = dynsec_roles__find(jtmp->valuestring);
dynsec_rolelists__add_role(&client->rolelist, role, priority);
dynsec_rolelists__client_add_role(client, role, priority);
}
}
}
@ -562,6 +570,23 @@ int dynsec_clients__process_set_password(cJSON *j_responses, struct mosquitto *c
}
static void client__add_new_roles(struct dynsec__client *client, struct dynsec__rolelist *base_rolelist)
{
struct dynsec__rolelist *rolelist, *rolelist_tmp;
HASH_ITER(hh, base_rolelist, rolelist, rolelist_tmp){
dynsec_rolelists__client_add_role(client, rolelist->role, rolelist->priority);
}
}
static void client__remove_all_roles(struct dynsec__client *client)
{
struct dynsec__rolelist *rolelist, *rolelist_tmp;
HASH_ITER(hh, client->rolelist, rolelist, rolelist_tmp){
dynsec_rolelists__client_remove_role(client, rolelist->role);
}
}
int dynsec_clients__process_modify(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data)
{
@ -607,8 +632,9 @@ int dynsec_clients__process_modify(cJSON *j_responses, struct mosquitto *context
rc = dynsec_rolelists__load_from_json(command, &rolelist);
if(rc == MOSQ_ERR_SUCCESS){
dynsec_rolelists__free_all(&client->rolelist);
client->rolelist = rolelist;
client__remove_all_roles(client);
client__add_new_roles(client, rolelist);
dynsec_rolelists__free_all(&rolelist);
}else if(rc == MOSQ_ERR_NOT_FOUND){
dynsec__command_reply(j_responses, context, "modifyClient", "Role not found", correlation_data);
dynsec_rolelists__free_all(&rolelist);
@ -848,16 +874,6 @@ int dynsec_clients__process_list(cJSON *j_responses, struct mosquitto *context,
}
void dynsec_clients__remove_role_from_all(const struct dynsec__role *role)
{
struct dynsec__client *client, *client_tmp;
HASH_ITER(hh, local_clients, client, client_tmp){
dynsec_rolelists__remove_role(&client->rolelist, role);
}
}
int dynsec_clients__process_add_role(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data)
{
char *username, *role_name;
@ -888,7 +904,7 @@ int dynsec_clients__process_add_role(cJSON *j_responses, struct mosquitto *conte
return MOSQ_ERR_SUCCESS;
}
dynsec_rolelists__add_role(&client->rolelist, role, priority);
dynsec_rolelists__client_add_role(client, role, priority);
dynsec__config_save();
dynsec__command_reply(j_responses, context, "addClientRole", NULL, correlation_data);
@ -927,7 +943,7 @@ int dynsec_clients__process_remove_role(cJSON *j_responses, struct mosquitto *co
return MOSQ_ERR_SUCCESS;
}
dynsec_rolelists__remove_role(&client->rolelist, role);
dynsec_rolelists__client_remove_role(client, role);
dynsec__config_save();
dynsec__command_reply(j_responses, context, "removeClientRole", NULL, correlation_data);

View File

@ -114,6 +114,8 @@ struct dynsec__acls{
struct dynsec__role{
UT_hash_handle hh;
struct dynsec__acls acls;
struct dynsec__clientlist *clientlist;
struct dynsec__grouplist *grouplist;
char *rolename;
char *text_name;
char *text_description;
@ -182,9 +184,10 @@ int dynsec_clients__process_modify(cJSON *j_responses, struct mosquitto *context
int dynsec_clients__process_remove_role(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data);
int dynsec_clients__process_set_password(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data);
struct dynsec__client *dynsec_clients__find(const char *username);
void dynsec_clients__remove_role_from_all(const struct dynsec__role *role);
cJSON *dynsec_clientlists__all_to_json(struct dynsec__clientlist *base_clientlist);
int dynsec_clientlist__cmp(void *a, void *b);
void dynsec_clientlist__kick_all(struct dynsec__clientlist *base_clientlist);
@ -211,9 +214,9 @@ int dynsec_groups__process_get_anonymous_group(cJSON *j_responses, struct mosqui
int dynsec_groups__process_set_anonymous_group(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data);
int dynsec_groups__remove_client(const char *username, const char *groupname, bool update_config);
struct dynsec__group *dynsec_groups__find(const char *groupname);
void dynsec_groups__remove_role_from_all(const struct dynsec__role *role);
cJSON *dynsec_grouplists__all_to_json(struct dynsec__grouplist *base_grouplist);
int dynsec_grouplist__cmp(void *a, void *b);
/* ################################################################
* #
@ -233,9 +236,11 @@ int dynsec_roles__process_modify(cJSON *j_responses, struct mosquitto *context,
int dynsec_roles__process_remove_acl(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data);
struct dynsec__role *dynsec_roles__find(const char *rolename);
int dynsec_rolelists__add_role(struct dynsec__rolelist **base_rolelist, struct dynsec__role *role, int priority);
int dynsec_rolelists__client_add_role(struct dynsec__client *client, struct dynsec__role *role, int priority);
int dynsec_rolelists__client_remove_role(struct dynsec__client *client, struct dynsec__role *role);
int dynsec_rolelists__group_add_role(struct dynsec__group *group, struct dynsec__role *role, int priority);
int dynsec_rolelists__group_remove_role(struct dynsec__group *group, struct dynsec__role *role);
int dynsec_rolelists__load_from_json(cJSON *command, struct dynsec__rolelist **rolelist);
int dynsec_rolelists__remove_role(struct dynsec__rolelist **base_rolelist, const struct dynsec__role *role);
void dynsec_rolelists__free_all(struct dynsec__rolelist **base_rolelist);
cJSON *dynsec_rolelists__all_to_json(struct dynsec__rolelist *base_rolelist);

View File

@ -68,7 +68,7 @@ static int group_cmp(void *a, void *b)
return strcmp(group_a->groupname, group_b->groupname);
}
static int grouplist_cmp(void *a, void *b)
int dynsec_grouplist__cmp(void *a, void *b)
{
struct dynsec__grouplist *grouplist_a = a;
struct dynsec__grouplist *grouplist_b = b;
@ -76,15 +76,7 @@ static int grouplist_cmp(void *a, void *b)
return grouplist_b->priority - grouplist_a->priority;
}
static int clientlist_cmp(void *a, void *b)
{
struct dynsec__clientlist *clientlist_a = a;
struct dynsec__clientlist *clientlist_b = b;
return strcmp(clientlist_a->username, clientlist_b->username);
}
static void clientlist__kick_all(struct dynsec__clientlist *base_clientlist)
void dynsec_clientlist__kick_all(struct dynsec__clientlist *base_clientlist)
{
struct dynsec__clientlist *clientlist, *clientlist_tmp;
@ -202,7 +194,7 @@ int dynsec_groups__process_add_role(cJSON *j_responses, struct mosquitto *contex
return MOSQ_ERR_SUCCESS;
}
dynsec_rolelists__add_role(&group->rolelist, role, priority);
dynsec_rolelists__group_add_role(group, role, priority);
dynsec__config_save();
dynsec__command_reply(j_responses, context, "addGroupRole", NULL, correlation_data);
return MOSQ_ERR_SUCCESS;
@ -302,7 +294,7 @@ int dynsec_groups__config_load(cJSON *tree)
if(j_rolename && cJSON_IsString(j_rolename)){
json_get_int(j_role, "priority", &priority, true, -1);
role = dynsec_roles__find(j_rolename->valuestring);
dynsec_rolelists__add_role(&group->rolelist, role, priority);
dynsec_rolelists__group_add_role(group, role, priority);
}
}
}
@ -493,7 +485,7 @@ int dynsec_groups__process_delete(cJSON *j_responses, struct mosquitto *context,
if(group == dynsec_anonymous_group){
mosquitto_kick_client_by_username(NULL, false);
}
clientlist__kick_all(group->clientlist);
dynsec_clientlist__kick_all(group->clientlist);
group__free_item(group);
dynsec__config_save();
@ -541,12 +533,12 @@ int dynsec_groups__add_client(const char *username, const char *groupname, int p
clientlist->username = client->username;
clientlist->client = client;
clientlist->priority = priority;
HASH_ADD_KEYPTR_INORDER(hh, group->clientlist, clientlist->username, strlen(clientlist->username), clientlist, clientlist_cmp);
HASH_ADD_KEYPTR_INORDER(hh, group->clientlist, clientlist->username, strlen(clientlist->username), clientlist, dynsec_clientlist__cmp);
grouplist->groupname = group->groupname;
grouplist->group = group;
grouplist->priority = priority;
HASH_ADD_KEYPTR_INORDER(hh, client->grouplist, grouplist->groupname, strlen(grouplist->groupname), grouplist, grouplist_cmp);
HASH_ADD_KEYPTR_INORDER(hh, client->grouplist, grouplist->groupname, strlen(grouplist->groupname), grouplist, dynsec_grouplist__cmp);
if(update_config){
dynsec__config_save();
@ -882,22 +874,6 @@ int dynsec_groups__process_get(cJSON *j_responses, struct mosquitto *context, cJ
}
void dynsec_groups__remove_role_from_all(const struct dynsec__role *role)
{
struct dynsec__group *group, *group_tmp;
struct dynsec__rolelist *rolelist;
HASH_ITER(hh, local_groups, group, group_tmp){
HASH_FIND(hh, group->rolelist, role->rolename, strlen(role->rolename), rolelist);
if(rolelist){
HASH_DELETE(hh, group->rolelist, rolelist);
mosquitto_free(rolelist->rolename);
mosquitto_free(rolelist);
}
}
}
int dynsec_groups__process_remove_role(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data)
{
char *groupname, *rolename;
@ -926,7 +902,7 @@ int dynsec_groups__process_remove_role(cJSON *j_responses, struct mosquitto *con
return MOSQ_ERR_SUCCESS;
}
dynsec_rolelists__remove_role(&group->rolelist, role);
dynsec_rolelists__group_remove_role(group, role);
dynsec__config_save();
dynsec__command_reply(j_responses, context, "removeGroupRole", NULL, correlation_data);
@ -934,7 +910,7 @@ int dynsec_groups__process_remove_role(cJSON *j_responses, struct mosquitto *con
if(group == dynsec_anonymous_group){
mosquitto_kick_client_by_username(NULL, false);
}
clientlist__kick_all(group->clientlist);
dynsec_clientlist__kick_all(group->clientlist);
return MOSQ_ERR_SUCCESS;
}
@ -1020,7 +996,7 @@ int dynsec_groups__process_modify(cJSON *j_responses, struct mosquitto *context,
if(group == dynsec_anonymous_group){
mosquitto_kick_client_by_username(NULL, false);
}
clientlist__kick_all(group->clientlist);
dynsec_clientlist__kick_all(group->clientlist);
return MOSQ_ERR_SUCCESS;
}

View File

@ -92,7 +92,48 @@ int dynsec_rolelists__remove_role(struct dynsec__rolelist **base_rolelist, const
}
int dynsec_rolelists__add_role(struct dynsec__rolelist **base_rolelist, struct dynsec__role *role, int priority)
int dynsec_rolelists__client_remove_role(struct dynsec__client *client, struct dynsec__role *role)
{
int rc;
struct dynsec__clientlist *found_clientlist;
rc = dynsec_rolelists__remove_role(&client->rolelist, role);
if(rc) return rc;
HASH_FIND(hh, role->clientlist, client->username, strlen(client->username), found_clientlist);
if(found_clientlist){
HASH_DELETE(hh, role->clientlist, found_clientlist);
mosquitto_free(found_clientlist->username);
mosquitto_free(found_clientlist);
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_NOT_FOUND;
}
}
int dynsec_rolelists__group_remove_role(struct dynsec__group *group, struct dynsec__role *role)
{
int rc;
struct dynsec__grouplist *found_grouplist;
rc = dynsec_rolelists__remove_role(&group->rolelist, role);
if(rc) return rc;
/* Remove group from role grouplist. */
HASH_FIND(hh, role->grouplist, group->groupname, strlen(group->groupname), found_grouplist);
if(found_grouplist){
HASH_DELETE(hh, role->grouplist, found_grouplist);
mosquitto_free(found_grouplist->groupname);
mosquitto_free(found_grouplist);
return MOSQ_ERR_SUCCESS;
}else{
return MOSQ_ERR_NOT_FOUND;
}
}
static int dynsec_rolelists__add_role(struct dynsec__rolelist **base_rolelist, struct dynsec__role *role, int priority)
{
struct dynsec__rolelist *rolelist;
@ -118,6 +159,74 @@ int dynsec_rolelists__add_role(struct dynsec__rolelist **base_rolelist, struct d
}
int dynsec_rolelists__client_add_role(struct dynsec__client *client, struct dynsec__role *role, int priority)
{
struct dynsec__rolelist *rolelist;
struct dynsec__clientlist *clientlist;
int rc;
rc = dynsec_rolelists__add_role(&client->rolelist, role, priority);
if(rc) return rc;
HASH_FIND(hh, client->rolelist, role->rolename, strlen(role->rolename), rolelist);
if(rolelist == NULL){
/* This should never happen because the above add_role succeeded. */
return MOSQ_ERR_UNKNOWN;
}
/* Add client to role clientlist */
clientlist = mosquitto_calloc(1, sizeof(struct dynsec__clientlist));
if(clientlist == NULL){
dynsec_rolelists__remove_role(&client->rolelist, role);
return MOSQ_ERR_NOMEM;
}
clientlist->client = client;
clientlist->username = mosquitto_strdup(client->username);
if(clientlist->username == NULL){
dynsec_rolelists__remove_role(&client->rolelist, role);
mosquitto_free(clientlist);
return MOSQ_ERR_NOMEM;
}
HASH_ADD_KEYPTR_INORDER(hh, role->clientlist, client->username, strlen(client->username), clientlist, dynsec_clientlist__cmp);
return MOSQ_ERR_SUCCESS;
}
int dynsec_rolelists__group_add_role(struct dynsec__group *group, struct dynsec__role *role, int priority)
{
struct dynsec__rolelist *rolelist;
struct dynsec__grouplist *grouplist;
int rc;
rc = dynsec_rolelists__add_role(&group->rolelist, role, priority);
if(rc) return rc;
HASH_FIND(hh, group->rolelist, role->rolename, strlen(role->rolename), rolelist);
if(rolelist == NULL){
/* This should never happen because the above add_role succeeded. */
return MOSQ_ERR_UNKNOWN;
}
/* Add group to role grouplist */
grouplist = mosquitto_calloc(1, sizeof(struct dynsec__grouplist));
if(grouplist == NULL){
dynsec_rolelists__remove_role(&group->rolelist, role);
return MOSQ_ERR_NOMEM;
}
grouplist->group = group;
grouplist->groupname = mosquitto_strdup(group->groupname);
if(grouplist->groupname == NULL){
dynsec_rolelists__remove_role(&group->rolelist, role);
mosquitto_free(grouplist);
return MOSQ_ERR_NOMEM;
}
HASH_ADD_KEYPTR_INORDER(hh, role->grouplist, group->groupname, strlen(group->groupname), grouplist, dynsec_grouplist__cmp);
return MOSQ_ERR_SUCCESS;
}
int dynsec_rolelists__load_from_json(cJSON *command, struct dynsec__rolelist **rolelist)
{
cJSON *j_roles, *j_role, *j_rolename;
@ -228,6 +337,21 @@ void dynsec_roles__cleanup(void)
}
static void role__kick_all(struct dynsec__role *role)
{
struct dynsec__grouplist *grouplist, *grouplist_tmp;
dynsec_clientlist__kick_all(role->clientlist);
HASH_ITER(hh, role->grouplist, grouplist, grouplist_tmp){
if(grouplist->group == dynsec_anonymous_group){
mosquitto_kick_client_by_username(NULL, false);
}
dynsec_clientlist__kick_all(grouplist->group->clientlist);
}
}
/* ################################################################
* #
* # Config file load and save
@ -528,6 +652,31 @@ error:
}
static void role__remove_all_clients(struct dynsec__role *role)
{
struct dynsec__clientlist *clientlist, *clientlist_tmp;
HASH_ITER(hh, role->clientlist, clientlist, clientlist_tmp){
mosquitto_kick_client_by_username(clientlist->username, false);
dynsec_rolelists__client_remove_role(clientlist->client, role);
}
}
static void role__remove_all_groups(struct dynsec__role *role)
{
struct dynsec__grouplist *grouplist, *grouplist_tmp;
HASH_ITER(hh, role->grouplist, grouplist, grouplist_tmp){
if(grouplist->group == dynsec_anonymous_group){
mosquitto_kick_client_by_username(NULL, false);
}
dynsec_clientlist__kick_all(grouplist->group->clientlist);
dynsec_rolelists__group_remove_role(grouplist->group, role);
}
}
int dynsec_roles__process_delete(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data)
{
char *rolename;
@ -540,8 +689,8 @@ int dynsec_roles__process_delete(cJSON *j_responses, struct mosquitto *context,
role = dynsec_roles__find(rolename);
if(role){
dynsec_clients__remove_role_from_all(role);
dynsec_groups__remove_role_from_all(role);
role__remove_all_clients(role);
role__remove_all_groups(role);
role__free_item(role, true);
dynsec__config_save();
dynsec__command_reply(j_responses, context, "deleteRole", NULL, correlation_data);
@ -736,6 +885,8 @@ int dynsec_roles__process_add_acl(cJSON *j_responses, struct mosquitto *context,
dynsec__config_save();
dynsec__command_reply(j_responses, context, "addRoleACL", NULL, correlation_data);
role__kick_all(role);
return MOSQ_ERR_SUCCESS;
}
@ -790,6 +941,8 @@ int dynsec_roles__process_remove_acl(cJSON *j_responses, struct mosquitto *conte
role__free_acl(acllist, acl);
dynsec__config_save();
dynsec__command_reply(j_responses, context, "removeRoleACL", NULL, correlation_data);
role__kick_all(role);
}else{
dynsec__command_reply(j_responses, context, "removeRoleACL", "ACL not found", correlation_data);
}