From a96c18eb17baac00fbde2ca051977ca1ebbd19f1 Mon Sep 17 00:00:00 2001 From: Erik Karlsson Date: Wed, 29 Jun 2022 12:50:47 +0200 Subject: [PATCH] mosquitto-auth-shadow: initial implementation Plugin for the mosquitto MQTT broker that allows authentication of users via /etc/shadow --- mosquitto-auth-shadow/Makefile | 45 +++++++++++ .../uci-defaults/14_set-shadow-permissions | 1 + mosquitto-auth-shadow/src/Makefile | 25 ++++++ .../src/mosquitto_auth_shadow.c | 80 +++++++++++++++++++ 4 files changed, 151 insertions(+) create mode 100644 mosquitto-auth-shadow/Makefile create mode 100644 mosquitto-auth-shadow/files/etc/uci-defaults/14_set-shadow-permissions create mode 100644 mosquitto-auth-shadow/src/Makefile create mode 100644 mosquitto-auth-shadow/src/mosquitto_auth_shadow.c diff --git a/mosquitto-auth-shadow/Makefile b/mosquitto-auth-shadow/Makefile new file mode 100644 index 000000000..6e442a3fd --- /dev/null +++ b/mosquitto-auth-shadow/Makefile @@ -0,0 +1,45 @@ +# +# Copyright (c) 2022 Genexis B.V. +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License 2.0 which is available at +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Erik Karlsson - initial implementation +# + +include $(TOPDIR)/rules.mk + +PKG_NAME:=mosquitto-auth-shadow +PKG_VERSION:=1.0.0 + +PKG_MAINTAINER:=Erik Karlsson +PKG_LICENSE:=EPL-2.0 + +PKG_BUILD_PARALLEL:=1 + +include $(INCLUDE_DIR)/package.mk + +define Package/mosquitto-auth-shadow + SECTION:=net + CATEGORY:=Network + TITLE:=mosquitto - /etc/shadow authentication plugin + DEPENDS:=mosquitto + USERID:=mosquitto=200:mosquitto=200 mosquitto=200:shadow=11 +endef + +define Package/mosquitto-auth-shadow/description + Plugin for the mosquitto MQTT message broker that authenticates + users using /etc/shadow +endef + +define Package/mosquitto-auth-shadow/install + $(INSTALL_DIR) $(1)/usr/lib + $(INSTALL_BIN) $(PKG_BUILD_DIR)/mosquitto_auth_shadow.so $(1)/usr/lib/ + $(CP) ./files/* $(1)/ +endef + +$(eval $(call BuildPackage,mosquitto-auth-shadow)) diff --git a/mosquitto-auth-shadow/files/etc/uci-defaults/14_set-shadow-permissions b/mosquitto-auth-shadow/files/etc/uci-defaults/14_set-shadow-permissions new file mode 100644 index 000000000..0492a043f --- /dev/null +++ b/mosquitto-auth-shadow/files/etc/uci-defaults/14_set-shadow-permissions @@ -0,0 +1 @@ +chown root:shadow /etc/shadow && chmod 0640 /etc/shadow diff --git a/mosquitto-auth-shadow/src/Makefile b/mosquitto-auth-shadow/src/Makefile new file mode 100644 index 000000000..63378032c --- /dev/null +++ b/mosquitto-auth-shadow/src/Makefile @@ -0,0 +1,25 @@ +# +# Copyright (c) 2022 Genexis B.V. +# +# This program and the accompanying materials are made available under the +# terms of the Eclipse Public License 2.0 which is available at +# https://www.eclipse.org/legal/epl-2.0/ +# +# SPDX-License-Identifier: EPL-2.0 +# +# Contributors: +# Erik Karlsson - initial implementation +# + +TARGETS = mosquitto_auth_shadow.so + +all: $(TARGETS) + +%.pic.o: %.c + $(CC) $(CFLAGS) -Wall -Werror -fPIC -c -o $@ $< + +mosquitto_auth_shadow.so: mosquitto_auth_shadow.pic.o + $(CC) $(LDFLAGS) -shared -o $@ $^ + +clean: + rm -f *.o $(TARGETS) diff --git a/mosquitto-auth-shadow/src/mosquitto_auth_shadow.c b/mosquitto-auth-shadow/src/mosquitto_auth_shadow.c new file mode 100644 index 000000000..575308e93 --- /dev/null +++ b/mosquitto-auth-shadow/src/mosquitto_auth_shadow.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022 Genexis B.V. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Erik Karlsson - initial implementation + */ + +#include +#include +#include +#include +#include +#include + +static int basic_auth_callback(int event, void *event_data, void *userdata) +{ + struct mosquitto_evt_basic_auth *ed = event_data; + struct spwd spbuf, *sp = NULL; + char buf[256]; + struct crypt_data data; + char *hash; + + /* Let other plugins or broker decide about anonymous login */ + if (ed->username == NULL) + return MOSQ_ERR_PLUGIN_DEFER; + + getspnam_r(ed->username, &spbuf, buf, sizeof(buf), &sp); + + if (sp == NULL || sp->sp_pwdp == NULL) + return MOSQ_ERR_AUTH; + + /* Empty string as hash means password is not required */ + if (sp->sp_pwdp[0] == 0) + return MOSQ_ERR_SUCCESS; + + if (ed->password == NULL) + return MOSQ_ERR_AUTH; + + memset(&data, 0, sizeof(data)); + hash = crypt_r(ed->password, sp->sp_pwdp, &data); + + if (hash == NULL) + return MOSQ_ERR_AUTH; + + if (strcmp(hash, sp->sp_pwdp) == 0) + return MOSQ_ERR_SUCCESS; + + return MOSQ_ERR_AUTH; +} + +int mosquitto_plugin_version(int supported_version_count, + const int *supported_versions) +{ + return 5; +} + +int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, + void **user_data, + struct mosquitto_opt *opts, int opt_count) +{ + *user_data = identifier; + + return mosquitto_callback_register(identifier, MOSQ_EVT_BASIC_AUTH, + basic_auth_callback, NULL, NULL); +} + +int mosquitto_plugin_cleanup(void *user_data, + struct mosquitto_opt *opts, int opt_count) +{ + mosquitto_plugin_id_t *identifier = user_data; + + return mosquitto_callback_unregister(identifier, MOSQ_EVT_BASIC_AUTH, + basic_auth_callback, NULL); +}