diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:49:46 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:49:46 +0000 |
commit | 50b37d4a27d3295a29afca2286f1a5a086142cec (patch) | |
tree | 9212f763934ee090ef72d823f559f52ce387f268 /src/modules/rlm_pam/rlm_pam.c | |
parent | Initial commit. (diff) | |
download | freeradius-upstream/3.2.1+dfsg.tar.xz freeradius-upstream/3.2.1+dfsg.zip |
Adding upstream version 3.2.1+dfsg.upstream/3.2.1+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/modules/rlm_pam/rlm_pam.c')
-rw-r--r-- | src/modules/rlm_pam/rlm_pam.c | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/src/modules/rlm_pam/rlm_pam.c b/src/modules/rlm_pam/rlm_pam.c new file mode 100644 index 0000000..70d8332 --- /dev/null +++ b/src/modules/rlm_pam/rlm_pam.c @@ -0,0 +1,244 @@ +/* + * This program is is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * $Id$ + * @file rlm_pam.c + * @brief Interfaces with the PAM library to allow auth via PAM. + * + * @note This was taken from the hacks that miguel a.l. paraz <map@iphil.net> + * did on radiusd-cistron-1.5.3 and migrated to a separate file. + * That, in fact, was again based on the original stuff from + * Jeph Blaize <jblaize@kiva.net> done in May 1997. + * + * @copyright 2000,2006 The FreeRADIUS server project + * @copyright 1997 Jeph Blaize <jblaize@kiva.net> + * @copyright 1999 miguel a.l. paraz <map@iphil.net> + */ +RCSID("$Id$") + +#include <freeradius-devel/radiusd.h> +#include <freeradius-devel/modules.h> + +#include "config.h" + +#ifdef HAVE_SECURITY_PAM_APPL_H +# include <security/pam_appl.h> +#endif + +#ifdef HAVE_PAM_PAM_APPL_H +# include <pam/pam_appl.h> +#endif + +#ifdef HAVE_SYSLOG_H +# include <syslog.h> +#endif + +typedef struct rlm_pam_t { + char const *pam_auth_name; +} rlm_pam_t; + +static const CONF_PARSER module_config[] = { + { "pam_auth", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_pam_t, pam_auth_name), "radiusd" }, + CONF_PARSER_TERMINATOR +}; + +typedef struct rlm_pam_data_t { + REQUEST *request; //!< The current request. + char const *username; //!< Username to provide to PAM when prompted. + char const *password; //!< Password to provide to PAM when prompted. + bool error; //!< True if pam_conv failed. +} rlm_pam_data_t; + +/** Dialogue between RADIUS and PAM modules + * + * Uses PAM's appdata_ptr so it's thread safe, and doesn't + * have any nasty static variables hanging around. + */ +static int pam_conv(int num_msg, struct pam_message const **msg, struct pam_response **resp, void *appdata_ptr) +{ + int count; + struct pam_response *reply; + REQUEST *request; + rlm_pam_data_t *pam_config = (rlm_pam_data_t *) appdata_ptr; + + request = pam_config->request; + +/* strdup(NULL) doesn't work on some platforms */ +#define COPY_STRING(s) ((s) ? strdup(s) : NULL) + + reply = rad_malloc(num_msg * sizeof(struct pam_response)); + memset(reply, 0, num_msg * sizeof(struct pam_response)); + for (count = 0; count < num_msg; count++) { + switch (msg[count]->msg_style) { + case PAM_PROMPT_ECHO_ON: + reply[count].resp_retcode = PAM_SUCCESS; + reply[count].resp = COPY_STRING(pam_config->username); + break; + + case PAM_PROMPT_ECHO_OFF: + reply[count].resp_retcode = PAM_SUCCESS; + reply[count].resp = COPY_STRING(pam_config->password); + break; + + case PAM_TEXT_INFO: + RDEBUG2("%s", msg[count]->msg); + break; + + case PAM_ERROR_MSG: + default: + RERROR("PAM conversation failed"); + /* Must be an error of some sort... */ + for (count = 0; count < num_msg; count++) { + if (msg[count]->msg_style == PAM_ERROR_MSG) RERROR("%s", msg[count]->msg); + if (reply[count].resp) { + /* could be a password, let's be sanitary */ + memset(reply[count].resp, 0, strlen(reply[count].resp)); + free(reply[count].resp); + } + } + free(reply); + pam_config->error = true; + return PAM_CONV_ERR; + } + } + *resp = reply; + /* PAM frees reply (including reply[].resp) */ + + return PAM_SUCCESS; +} + +/** Check the users password against the standard UNIX password table + PAM. + * + * @note For most flexibility, passing a pamauth type to this function + * allows you to have multiple authentication types (i.e. multiple + * files associated with radius in /etc/pam.d). + * + * @param request The current request. + * @param username User to authenticate. + * @param passwd Password to authenticate with, + * @param pamauth Type of PAM authentication. + * @return 0 on success -1 on failure. + */ +static int do_pam(REQUEST *request, char const *username, char const *passwd, char const *pamauth) +{ + pam_handle_t *handle = NULL; + int ret; + rlm_pam_data_t pam_config; + struct pam_conv conv; + + /* + * Initialize the structures + */ + conv.conv = pam_conv; + conv.appdata_ptr = &pam_config; + pam_config.request = request; + pam_config.username = username; + pam_config.password = passwd; + pam_config.error = false; + + RDEBUG2("Using pamauth string \"%s\" for pam.conf lookup", pamauth); + + ret = pam_start(pamauth, username, &conv, &handle); + if (ret != PAM_SUCCESS) { + RERROR("pam_start failed: %s", pam_strerror(handle, ret)); + return -1; + } + + ret = pam_authenticate(handle, 0); + if (ret != PAM_SUCCESS) { + RERROR("pam_authenticate failed: %s", pam_strerror(handle, ret)); + pam_end(handle, ret); + return -1; + } + + /* + * FreeBSD 3.x doesn't have account and session management + * functions in PAM, while 4.0 does. + */ +#if !defined(__FreeBSD_version) || (__FreeBSD_version >= 400000) + ret = pam_acct_mgmt(handle, 0); + if (ret != PAM_SUCCESS) { + RERROR("pam_acct_mgmt failed: %s", pam_strerror(handle, ret)); + pam_end(handle, ret); + return -1; + } +#endif + RDEBUG2("Authentication succeeded"); + pam_end(handle, ret); + return 0; +} + +static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request) +{ + int ret; + VALUE_PAIR *pair; + rlm_pam_t *data = (rlm_pam_t *) instance; + + char const *pam_auth_string = data->pam_auth_name; + + /* + * We can only authenticate user requests which HAVE + * a User-Name attribute. + */ + if (!request->username) { + RAUTH("Attribute \"User-Name\" is required for authentication"); + return RLM_MODULE_INVALID; + } + + /* + * We can only authenticate user requests which HAVE + * a User-Password attribute. + */ + if (!request->password) { + RAUTH("Attribute \"User-Password\" is required for authentication"); + return RLM_MODULE_INVALID; + } + + /* + * Ensure that we're being passed a plain-text password, + * and not anything else. + */ + if (request->password->da->attr != PW_USER_PASSWORD) { + RAUTH("Attribute \"User-Password\" is required for authentication. Cannot use \"%s\".", request->password->da->name); + return RLM_MODULE_INVALID; + } + + /* + * Let the 'users' file over-ride the PAM auth name string, + * for backwards compatibility. + */ + pair = fr_pair_find_by_num(request->config, PW_PAM_AUTH, 0, TAG_ANY); + if (pair) pam_auth_string = pair->vp_strvalue; + + ret = do_pam(request, request->username->vp_strvalue, request->password->vp_strvalue, pam_auth_string); + if (ret < 0) return RLM_MODULE_REJECT; + + return RLM_MODULE_OK; +} + +extern module_t rlm_pam; +module_t rlm_pam = { + .magic = RLM_MODULE_INIT, + .name = "pam", + .type = RLM_TYPE_THREAD_UNSAFE, /* The PAM libraries are not thread-safe */ + .inst_size = sizeof(rlm_pam_t), + .config = module_config, + .methods = { + [MOD_AUTHENTICATE] = mod_authenticate + }, +}; + |