/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 2002-2021 Free Software Foundation, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include #ifdef HAVE_SHADOW_H # include #endif #include #include #include #include #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_SECURITY_PAM_APPL_H # include #endif #ifdef HAVE_CRYPT_H # include #endif #include #include #include #include #include #include char *mu_pam_service = PACKAGE; #ifdef USE_LIBPAM static struct mu_cfg_param mu_pam_param[] = { { "service", mu_c_string, &mu_pam_service, 0, NULL, N_("Set PAM service name."), N_("name") }, { NULL } }; #define COPY_STRING(s) (s) ? strdup(s) : NULL static char *_pwd; static char *_user; #define overwrite_and_free(ptr) \ do \ { \ char *s = ptr; \ while (*s) \ *s++ = 0; \ } \ while (0) #ifndef PAM_AUTHTOK_RECOVER_ERR # define PAM_AUTHTOK_RECOVER_ERR PAM_CONV_ERR #endif static int mu_pam_conv (int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr MU_ARG_UNUSED) { int status = PAM_SUCCESS; int i; struct pam_response *reply = NULL; reply = calloc (num_msg, sizeof (*reply)); if (!reply) return PAM_CONV_ERR; for (i = 0; i < num_msg && status == PAM_SUCCESS; i++) { switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_ON: reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = COPY_STRING (_user); /* PAM frees resp */ break; case PAM_PROMPT_ECHO_OFF: if (_pwd) { reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = COPY_STRING (_pwd); /* PAM frees resp */ } else status = PAM_AUTHTOK_RECOVER_ERR; break; case PAM_TEXT_INFO: case PAM_ERROR_MSG: reply[i].resp_retcode = PAM_SUCCESS; reply[i].resp = NULL; break; default: status = PAM_CONV_ERR; } } if (status != PAM_SUCCESS) { for (i = 0; i < num_msg; i++) if (reply[i].resp) { switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_ON: case PAM_PROMPT_ECHO_OFF: overwrite_and_free (reply[i].resp); break; case PAM_ERROR_MSG: case PAM_TEXT_INFO: free (reply[i].resp); } } free (reply); } else *resp = reply; return status; } static struct pam_conv PAM_conversation = { &mu_pam_conv, NULL }; int mu_authenticate_pam (struct mu_auth_data **return_data MU_ARG_UNUSED, const void *key, void *func_data MU_ARG_UNUSED, void *call_data) { const struct mu_auth_data *auth_data = key; char *pass = call_data; pam_handle_t *pamh; int pamerror; #define PAM_ERROR if (pamerror != PAM_SUCCESS) goto pam_errlab; if (!auth_data) return EINVAL; _user = (char *) auth_data->name; _pwd = pass; pamerror = pam_start (mu_pam_service, _user, &PAM_conversation, &pamh); PAM_ERROR; pamerror = pam_authenticate (pamh, 0); PAM_ERROR; pamerror = pam_acct_mgmt (pamh, 0); PAM_ERROR; pamerror = pam_setcred (pamh, PAM_ESTABLISH_CRED); pam_errlab: pam_end (pamh, PAM_SUCCESS); switch (pamerror) { case PAM_SUCCESS: return 0; case PAM_AUTH_ERR: return MU_ERR_AUTH_FAILURE; } return MU_ERR_FAILURE; } #else # define mu_pam_param NULL # define mu_authenticate_pam NULL #endif struct mu_auth_module mu_auth_pam_module = { .name = "pam", .handler = { [mu_auth_authenticate] = mu_authenticate_pam }, .cfg = mu_pam_param };