Move password file code to own plugin

This commit is contained in:
Roger A. Light
2025-07-10 14:49:46 +01:00
parent c83ee48e5c
commit 0b4ab2f285
25 changed files with 543 additions and 258 deletions
+5
View File
@@ -41,6 +41,7 @@ option(WITH_PLUGIN_ACL_FILE "Build acl-file plugin?" ON)
option(WITH_PLUGIN_DYNAMIC_SECURITY "Build dynamic-security plugin?" ON)
option(WITH_PLUGIN_EXAMPLES "Build example plugins?" ON)
option(WITH_PLUGIN_PERSIST_SQLITE "Build persist-sqlite plugin?" ON)
option(WITH_PLUGIN_PASSWORD_FILE "Build password-file plugin?" ON)
option(WITH_PLUGIN_SPARKPLUG_AWARE "Build sparkplug-aware plugin?" ON)
if(WITH_PLUGIN_ACL_FILE)
@@ -55,6 +56,10 @@ if (WITH_PLUGIN_EXAMPLES)
add_subdirectory(examples)
endif()
if(WITH_PLUGIN_PASSWORD_FILE)
add_subdirectory(password-file)
endif()
if (WITH_PLUGIN_PERSIST_SQLITE)
find_package(SQLite3 REQUIRED)
add_subdirectory(persist-sqlite)
+1
View File
@@ -2,6 +2,7 @@ DIRS= \
acl-file \
dynamic-security \
examples \
password-file \
persist-sqlite \
sparkplug-aware
+12
View File
@@ -0,0 +1,12 @@
set(PLUGIN_NAME mosquitto_password_file)
set(SRCLIST
password_check.c
password_parse.c
plugin.c
)
set(INCLIST ${mosquitto_SOURCE_DIR}/src)
set(LINKLIST libmosquitto_common)
add_mosquitto_plugin("${PLUGIN_NAME}" "${SRCLIST}" "${INCLIST}" "${LINKLIST}")
+21
View File
@@ -0,0 +1,21 @@
R=../..
include ${R}/config.mk
PLUGIN_NAME=mosquitto_password_file
LOCAL_CFLAGS+=
LOCAL_CPPFLAGS+=-I${R}/src
LOCAL_LDFLAGS+=
LOCAL_LIBADD+=${R}/libcommon/libmosquitto_common.so.${SOVERSION}
# Objects for this plugin only, built from source in this directory
OBJS = \
password_check.o \
password_parse.o \
plugin.o
# Objects from e.g. the common directory that are not in this directory
OBJS_EXTERNAL =
all : binary
include ${R}/plugins/plugin.mk
+54
View File
@@ -0,0 +1,54 @@
/*
Copyright (c) 2011-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 <uthash.h>
#include "mosquitto.h"
#include "password_file.h"
int password_file__check(int event, void *event_data, void *userdata)
{
struct mosquitto_evt_basic_auth *ed = event_data;
struct password_file_data *data = userdata;
struct mosquitto__unpwd *u;
UNUSED(event);
if(ed->username == NULL){
return MOSQ_ERR_PLUGIN_IGNORE;
}
// FIXME if(ed->client->bridge) return MOSQ_ERR_SUCCESS;
HASH_FIND(hh, data->unpwd, ed->username, strlen(ed->username), u);
if(u){
if(u->pw){
if(ed->password){
return mosquitto_pw_verify(u->pw, ed->password);
}else{
return MOSQ_ERR_AUTH;
}
}else{
return MOSQ_ERR_SUCCESS;
}
}
return MOSQ_ERR_AUTH;
}
+137
View File
@@ -0,0 +1,137 @@
/*
Copyright (c) 2011-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 <ctype.h>
#include <stdio.h>
#include <string.h>
#include "mosquitto.h"
#include "password_file.h"
int password_file__parse(struct password_file_data *data)
{
FILE *pwfile;
struct mosquitto__unpwd *unpwd;
char *username, *password;
char *saveptr = NULL;
char *buf;
int buflen = 256;
buf = mosquitto_malloc((size_t)buflen);
if(buf == NULL){
mosquitto_log_printf(MOSQ_LOG_ERR, "Error: Out of memory.");
return MOSQ_ERR_NOMEM;
}
pwfile = mosquitto_fopen(data->password_file, "rt", true);
if(!pwfile){
mosquitto_log_printf(MOSQ_LOG_ERR, "password-file: Error: Unable to open pwfile \"%s\".", data->password_file);
mosquitto_FREE(buf);
return MOSQ_ERR_UNKNOWN;
}
while(!feof(pwfile)){
if(mosquitto_fgets(&buf, &buflen, pwfile)){
if(buf[0] == '#') continue;
if(!strchr(buf, ':')) continue;
username = strtok_r(buf, ":", &saveptr);
if(username){
username = mosquitto_trimblanks(username);
if(strlen(username) > 65535){
mosquitto_log_printf(MOSQ_LOG_NOTICE, "password-file: Warning: Invalid line in password file '%s', username too long.", data->password_file);
continue;
}
if(strlen(username) <= 0){
mosquitto_log_printf(MOSQ_LOG_NOTICE, "password-file: Warning: Empty username in password file '%s', ignoring.", data->password_file);
continue;
}
HASH_FIND(hh, data->unpwd, username, strlen(username), unpwd);
if(unpwd){
mosquitto_log_printf(MOSQ_LOG_NOTICE, "password-file: Error: Duplicate user '%s' in password file '%s', ignoring.", username, data->password_file);
continue;
}
unpwd = mosquitto_calloc(1, sizeof(struct mosquitto__unpwd));
if(!unpwd){
fclose(pwfile);
mosquitto_FREE(buf);
return MOSQ_ERR_NOMEM;
}
unpwd->username = mosquitto_strdup(username);
if(!unpwd->username){
mosquitto_FREE(unpwd);
mosquitto_FREE(buf);
fclose(pwfile);
return MOSQ_ERR_NOMEM;
}
password = strtok_r(NULL, ":", &saveptr);
if(password){
password = mosquitto_trimblanks(password);
if(strlen(password) > 65535){
mosquitto_log_printf(MOSQ_LOG_NOTICE, "password-file: Warning: Invalid line in password file '%s', password too long.", data->password_file);
mosquitto_FREE(unpwd->username);
mosquitto_FREE(unpwd);
continue;
}
if(mosquitto_pw_new(&unpwd->pw, MOSQ_PW_DEFAULT)
|| mosquitto_pw_decode(unpwd->pw, password)){
mosquitto_log_printf(MOSQ_LOG_NOTICE, "password-file: Warning: Unable to decode line in password file '%s'.", data->password_file);
mosquitto_pw_cleanup(unpwd->pw);
mosquitto_FREE(unpwd->username);
mosquitto_FREE(unpwd);
continue;
}
HASH_ADD_KEYPTR(hh, data->unpwd, unpwd->username, strlen(unpwd->username), unpwd);
}else{
mosquitto_log_printf(MOSQ_LOG_NOTICE, "password-file: Warning: Invalid line in password file '%s': %s", data->password_file, buf);
mosquitto_pw_cleanup(unpwd->pw);
mosquitto_FREE(unpwd->username);
mosquitto_FREE(unpwd);
}
}
}
}
fclose(pwfile);
mosquitto_FREE(buf);
return MOSQ_ERR_SUCCESS;
}
void password_file__cleanup(struct password_file_data *data)
{
struct mosquitto__unpwd *u, *tmp = NULL;
if(!data) return;
HASH_ITER(hh, data->unpwd, u, tmp){
HASH_DEL(data->unpwd, u);
mosquitto_pw_cleanup(u->pw);
mosquitto_FREE(u->username);
mosquitto_FREE(u);
}
}
+79
View File
@@ -0,0 +1,79 @@
/*
Copyright (c) 2025 Cedalo Gmbh
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "mosquitto.h"
#include "password_file.h"
#define PLUGIN_NAME "password-file"
MOSQUITTO_PLUGIN_DECLARE_VERSION(5);
static mosquitto_plugin_id_t *mosq_pid = NULL;
static int handle_options(struct password_file_data *data, struct mosquitto_opt *options, int option_count)
{
for(int i=0; i<option_count; i++){
if(!strcmp(options[i].key, "password_file")){
mosquitto_FREE(data->password_file);
data->password_file = mosquitto_strdup(options[i].value);
if(!data->password_file){
return MOSQ_ERR_NOMEM;
}
}else{
mosquitto_log_printf(MOSQ_LOG_ERR, PLUGIN_NAME ": Error: Unknown option '%s'.", options[i].key);
return MOSQ_ERR_INVAL;
}
}
return MOSQ_ERR_SUCCESS;
}
int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *options, int option_count)
{
struct password_file_data *data;
int rc;
UNUSED(options);
UNUSED(option_count);
data = mosquitto_calloc(1, sizeof(struct password_file_data));
if(!data){
return MOSQ_ERR_NOMEM;
}
*user_data = data;
mosq_pid = identifier;
mosquitto_plugin_set_info(identifier, PLUGIN_NAME, NULL);
rc = handle_options(data, options, option_count);
if(rc) return rc;
rc = password_file__parse(data);
if(rc) return rc;
rc = mosquitto_callback_register(mosq_pid, MOSQ_EVT_BASIC_AUTH, password_file__check, NULL, data);
if(rc) return rc;
return MOSQ_ERR_SUCCESS;
}
int mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *options, int option_count)
{
struct password_file_data *data = user_data;
UNUSED(options);
UNUSED(option_count);
mosquitto_callback_unregister(mosq_pid, MOSQ_EVT_BASIC_AUTH, password_file__check, NULL);
password_file__cleanup(data);
return MOSQ_ERR_SUCCESS;
}
+4
View File
@@ -0,0 +1,4 @@
listener 1883
allow_anonymous true
plugin ./mosquitto_password_file.so
plugin_opt_password_file ./test.pwfile
+1
View File
@@ -0,0 +1 @@
user:$7$1000$h0tqVxBwkB9rKAXukTtffzdbBQtNy1q5FBTDwSW4hucfjpqunBbxW10NVnRk7Cfh0lQndnOv2+k4wJavgz1JNw==$02ujkUXlKkJGFzlQHNjUgXwG3XRB1mr3vs8NX5teGCJGbN4hdgSpHNHuj47j8r5SHXsO7GeHpmkpPNhLraVVcQ==
+2
View File
@@ -0,0 +1,2 @@
VG="valgrind --log-file=vglog"
${VG} ../../src/mosquitto -c test.conf -v