/* * 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 #include #include #include #include #include #include #ifdef ENABLE_PAM_SUPPORT #include 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); }