2 * This program is is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * @brief Interfaces with the PAM library to allow auth via PAM.
22 * @note This was taken from the hacks that miguel a.l. paraz <map@iphil.net>
23 * did on radiusd-cistron-1.5.3 and migrated to a separate file.
24 * That, in fact, was again based on the original stuff from
25 * Jeph Blaize <jblaize@kiva.net> done in May 1997.
27 * @copyright 2000,2006 The FreeRADIUS server project
28 * @copyright 1997 Jeph Blaize <jblaize@kiva.net>
29 * @copyright 1999 miguel a.l. paraz <map@iphil.net>
33 #include <freeradius-devel/radiusd.h>
34 #include <freeradius-devel/modules.h>
38 #ifdef HAVE_SECURITY_PAM_APPL_H
39 # include <security/pam_appl.h>
42 #ifdef HAVE_PAM_PAM_APPL_H
43 # include <pam/pam_appl.h>
50 typedef struct rlm_pam_t {
51 char const *pam_auth_name;
54 static const CONF_PARSER module_config[] = {
55 { "pam_auth", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_pam_t, pam_auth_name), "radiusd" },
56 { NULL, -1, 0, NULL, NULL }
59 typedef struct rlm_pam_data_t {
60 REQUEST *request; //!< The current request.
61 char const *username; //!< Username to provide to PAM when prompted.
62 char const *password; //!< Password to provide to PAM when prompted.
63 bool error; //!< True if pam_conv failed.
66 /** Dialogue between RADIUS and PAM modules
68 * Uses PAM's appdata_ptr so it's thread safe, and doesn't
69 * have any nasty static variables hanging around.
71 static int pam_conv(int num_msg, struct pam_message const **msg, struct pam_response **resp, void *appdata_ptr)
74 struct pam_response *reply;
76 rlm_pam_data_t *pam_config = (rlm_pam_data_t *) appdata_ptr;
78 request = pam_config->request;
80 /* strdup(NULL) doesn't work on some platforms */
81 #define COPY_STRING(s) ((s) ? strdup(s) : NULL)
83 reply = rad_malloc(num_msg * sizeof(struct pam_response));
84 memset(reply, 0, num_msg * sizeof(struct pam_response));
85 for (count = 0; count < num_msg; count++) {
86 switch (msg[count]->msg_style) {
87 case PAM_PROMPT_ECHO_ON:
88 reply[count].resp_retcode = PAM_SUCCESS;
89 reply[count].resp = COPY_STRING(pam_config->username);
92 case PAM_PROMPT_ECHO_OFF:
93 reply[count].resp_retcode = PAM_SUCCESS;
94 reply[count].resp = COPY_STRING(pam_config->password);
98 RDEBUG2("%s", msg[count]->msg);
103 RERROR("PAM conversation failed");
104 /* Must be an error of some sort... */
105 for (count = 0; count < num_msg; count++) {
106 if (msg[count]->msg_style == PAM_ERROR_MSG) RERROR("%s", msg[count]->msg);
107 if (reply[count].resp) {
108 /* could be a password, let's be sanitary */
109 memset(reply[count].resp, 0, strlen(reply[count].resp));
110 free(reply[count].resp);
114 pam_config->error = true;
119 /* PAM frees reply (including reply[].resp) */
124 /** Check the users password against the standard UNIX password table + PAM.
126 * @note For most flexibility, passing a pamauth type to this function
127 * allows you to have multiple authentication types (i.e. multiple
128 * files associated with radius in /etc/pam.d).
130 * @param request The current request.
131 * @param username User to authenticate.
132 * @param passwd Password to authenticate with,
133 * @param pamauth Type of PAM authentication.
134 * @return 0 on success -1 on failure.
136 static int do_pam(REQUEST *request, char const *username, char const *passwd, char const *pamauth)
138 pam_handle_t *handle = NULL;
140 rlm_pam_data_t pam_config;
141 struct pam_conv conv;
144 * Initialize the structures
146 conv.conv = pam_conv;
147 conv.appdata_ptr = &pam_config;
148 pam_config.request = request;
149 pam_config.username = username;
150 pam_config.password = passwd;
151 pam_config.error = false;
153 RDEBUG2("Using pamauth string \"%s\" for pam.conf lookup", pamauth);
155 ret = pam_start(pamauth, username, &conv, &handle);
156 if (ret != PAM_SUCCESS) {
157 RERROR("pam_start failed: %s", pam_strerror(handle, ret));
161 ret = pam_authenticate(handle, 0);
162 if (ret != PAM_SUCCESS) {
163 RERROR("pam_authenticate failed: %s", pam_strerror(handle, ret));
164 pam_end(handle, ret);
169 * FreeBSD 3.x doesn't have account and session management
170 * functions in PAM, while 4.0 does.
172 #if !defined(__FreeBSD_version) || (__FreeBSD_version >= 400000)
173 ret = pam_acct_mgmt(handle, 0);
174 if (ret != PAM_SUCCESS) {
175 RERROR("pam_acct_mgmt failed: %s", pam_strerror(handle, ret));
176 pam_end(handle, ret);
180 RDEBUG2("Authentication succeeded");
181 pam_end(handle, ret);
185 static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
189 rlm_pam_t *data = (rlm_pam_t *) instance;
191 char const *pam_auth_string = data->pam_auth_name;
194 * We can only authenticate user requests which HAVE
195 * a User-Name attribute.
197 if (!request->username) {
198 RAUTH("Attribute \"User-Name\" is required for authentication");
199 return RLM_MODULE_INVALID;
203 * We can only authenticate user requests which HAVE
204 * a User-Password attribute.
206 if (!request->password) {
207 RAUTH("Attribute \"User-Password\" is required for authentication");
208 return RLM_MODULE_INVALID;
212 * Ensure that we're being passed a plain-text password,
213 * and not anything else.
215 if (request->password->da->attr != PW_USER_PASSWORD) {
216 RAUTH("Attribute \"User-Password\" is required for authentication. Cannot use \"%s\".", request->password->da->name);
217 return RLM_MODULE_INVALID;
221 * Let the 'users' file over-ride the PAM auth name string,
222 * for backwards compatibility.
224 pair = fr_pair_find_by_num(request->config, PW_PAM_AUTH, 0, TAG_ANY);
225 if (pair) pam_auth_string = pair->vp_strvalue;
227 ret = do_pam(request, request->username->vp_strvalue, request->password->vp_strvalue, pam_auth_string);
228 if (ret < 0) return RLM_MODULE_REJECT;
230 return RLM_MODULE_OK;
233 extern module_t rlm_pam;
235 .magic = RLM_MODULE_INIT,
237 .type = RLM_TYPE_THREAD_UNSAFE, /* The PAM libraries are not thread-safe */
238 .inst_size = sizeof(rlm_pam_t),
239 .config = module_config,
241 [MOD_AUTHENTICATE] = mod_authenticate