mirror of
https://dev.iopsys.eu/feed/iopsys.git
synced 2025-12-10 07:44:50 +01:00
153 lines
3.8 KiB
C
153 lines
3.8 KiB
C
/*
|
|
* 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
|
|
*/
|
|
|
|
#define _GNU_SOURCE
|
|
#include <string.h>
|
|
#include <shadow.h>
|
|
#include <crypt.h>
|
|
#include <stdlib.h>
|
|
#include <mosquitto.h>
|
|
#include <mosquitto_broker.h>
|
|
#include <mosquitto_plugin.h>
|
|
|
|
#ifdef ENABLE_PAM_SUPPORT
|
|
#include <security/pam_appl.h>
|
|
|
|
static int pam_conversation(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
|
|
{
|
|
int i;
|
|
const char *pass = (const char *)appdata_ptr;
|
|
|
|
*resp = calloc(num_msg, sizeof(struct pam_response));
|
|
if (*resp == NULL) {
|
|
mosquitto_log_printf(MOSQ_LOG_ERR, "pam failed to allocate buffer for validation");
|
|
return PAM_BUF_ERR;
|
|
}
|
|
|
|
if (pass == NULL)
|
|
return PAM_SUCCESS;
|
|
|
|
for (i = 0; i < num_msg; ++i) {
|
|
if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF) {
|
|
(*resp)[i].resp = strdup(pass);
|
|
if ((*resp)[i].resp == NULL) {
|
|
for (int j = 0; j < i ; j++)
|
|
free((*resp)[j].resp);
|
|
|
|
free(*resp);
|
|
*resp = NULL;
|
|
mosquitto_log_printf(MOSQ_LOG_ERR, "pam failed in strdup");
|
|
return PAM_BUF_ERR;
|
|
}
|
|
}
|
|
}
|
|
return PAM_SUCCESS;
|
|
}
|
|
|
|
static int process_pam_auth_callback(struct mosquitto_evt_basic_auth *ed)
|
|
{
|
|
struct pam_conv conv;
|
|
int retval;
|
|
pam_handle_t *pamh = NULL;
|
|
|
|
conv.conv = pam_conversation;
|
|
conv.appdata_ptr = (void *)ed->password;
|
|
|
|
retval = pam_start("mosquitto", ed->username, &conv, &pamh);
|
|
if (retval != PAM_SUCCESS) {
|
|
mosquitto_log_printf(MOSQ_LOG_ERR, "pam start failed: %s", pam_strerror(pamh, retval));
|
|
return MOSQ_ERR_AUTH;
|
|
}
|
|
|
|
retval = pam_authenticate(pamh, 0);
|
|
pam_end(pamh, retval);
|
|
if (retval == PAM_SUCCESS) {
|
|
mosquitto_log_printf(MOSQ_LOG_NOTICE, "pam user [%s] logged in", ed->username);
|
|
return MOSQ_ERR_SUCCESS;
|
|
}
|
|
|
|
mosquitto_log_printf(MOSQ_LOG_NOTICE, "pam user [%s] failed authentication, err [%s]", ed->username, pam_strerror(pamh, retval));
|
|
return MOSQ_ERR_AUTH;
|
|
}
|
|
#else
|
|
static int process_shadow_auth_callback(struct mosquitto_evt_basic_auth *ed)
|
|
{
|
|
struct spwd spbuf, *sp = NULL;
|
|
char buf[256];
|
|
struct crypt_data data;
|
|
char *hash;
|
|
|
|
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;
|
|
}
|
|
#endif
|
|
|
|
static int basic_auth_callback(int event, void *event_data, void *userdata)
|
|
{
|
|
struct mosquitto_evt_basic_auth *ed = event_data;
|
|
|
|
/* Let other plugins or broker decide about anonymous login */
|
|
if (ed->username == NULL)
|
|
return MOSQ_ERR_PLUGIN_DEFER;
|
|
|
|
#ifdef ENABLE_PAM_SUPPORT
|
|
return process_pam_auth_callback(ed);
|
|
#else
|
|
return process_shadow_auth_callback(ed);
|
|
#endif
|
|
}
|
|
|
|
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);
|
|
}
|