The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_pam.c
Go to the documentation of this file.
1 /*
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.
6  *
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.
11  *
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
15  */
16 
17 /**
18  * $Id: 9686d20a9d38e616f127d4abdf342911220b08ea $
19  * @file rlm_pam.c
20  * @brief Interfaces with the PAM library to allow auth via PAM.
21  *
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.
26  *
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)
30  */
31 RCSID("$Id: 9686d20a9d38e616f127d4abdf342911220b08ea $")
32 
33 #include <freeradius-devel/server/base.h>
34 #include <freeradius-devel/server/module_rlm.h>
35 
36 #include "config.h"
37 
38 #ifdef HAVE_SECURITY_PAM_APPL_H
39 # include <security/pam_appl.h>
40 #endif
41 
42 #ifdef HAVE_PAM_PAM_APPL_H
43 # include <pam/pam_appl.h>
44 #endif
45 
46 #ifdef HAVE_SYSLOG_H
47 # include <syslog.h>
48 #endif
49 
50 typedef struct {
51  char const *pam_auth_name;
52 } rlm_pam_t;
53 
54 typedef struct {
55  request_t *request; //!< The current request.
56  char const *username; //!< Username to provide to PAM when prompted.
57  char const *password; //!< Password to provide to PAM when prompted.
58  bool error; //!< True if pam_conv failed.
60 
61 static const conf_parser_t module_config[] = {
62  { FR_CONF_OFFSET("pam_auth", rlm_pam_t, pam_auth_name) },
64 };
65 
66 static fr_dict_t const *dict_freeradius;
67 static fr_dict_t const *dict_radius;
68 
71  { .out = &dict_freeradius, .proto = "freeradius" },
72  { .out = &dict_radius, .proto = "radius" },
73  { NULL }
74 };
75 
79 
82  { .out = &attr_pam_auth, .name = "Pam-Auth", .type = FR_TYPE_STRING, .dict = &dict_freeradius },
83  { .out = &attr_user_name, .name = "User-Name", .type = FR_TYPE_STRING, .dict = &dict_radius },
84  { .out = &attr_user_password, .name = "User-Password", .type = FR_TYPE_STRING, .dict = &dict_radius },
85  { NULL }
86 };
87 
88 static int mod_instantiate(module_inst_ctx_t const *mctx)
89 {
90  rlm_pam_t *inst = talloc_get_type_abort(mctx->mi->data, rlm_pam_t);
91 
92  if (!inst->pam_auth_name) inst->pam_auth_name = main_config->name;
93 
94  return 0;
95 }
96 
97 /** Dialogue between RADIUS and PAM modules
98  *
99  * Uses PAM's appdata_ptr so it's thread safe, and doesn't
100  * have any nasty static variables hanging around.
101  */
102 static int pam_conv(int num_msg, struct pam_message const **msg, struct pam_response **resp, void *appdata_ptr)
103 {
104  int count;
105  struct pam_response *reply;
106  request_t *request;
107  rlm_pam_data_t *pam_config = (rlm_pam_data_t *) appdata_ptr;
108 
109  request = pam_config->request;
110 
111 #define COPY_STRING(s) ((s) ? talloc_strdup(reply, s) : NULL)
112  MEM(reply = talloc_zero_array(NULL, struct pam_response, num_msg));
113  for (count = 0; count < num_msg; count++) {
114  switch (msg[count]->msg_style) {
115  case PAM_PROMPT_ECHO_ON:
116  reply[count].resp_retcode = PAM_SUCCESS;
117  reply[count].resp = COPY_STRING(pam_config->username);
118  break;
119 
120  case PAM_PROMPT_ECHO_OFF:
121  reply[count].resp_retcode = PAM_SUCCESS;
122  reply[count].resp = COPY_STRING(pam_config->password);
123  break;
124 
125  case PAM_TEXT_INFO:
126  RDEBUG2("%s", msg[count]->msg);
127  break;
128 
129  case PAM_ERROR_MSG:
130  default:
131  RERROR("PAM conversation failed");
132  /* Must be an error of some sort... */
133  for (count = 0; count < num_msg; count++) {
134  if (msg[count]->msg_style == PAM_ERROR_MSG) RERROR("%s", msg[count]->msg);
135  if (reply[count].resp) {
136  /* could be a password, let's be sanitary */
137  memset(reply[count].resp, 0, strlen(reply[count].resp));
138  }
139  }
140  talloc_free(reply);
141  pam_config->error = true;
142  return PAM_CONV_ERR;
143  }
144  }
145  *resp = reply;
146  /* PAM frees reply (including reply[].resp) */
147 
148  return PAM_SUCCESS;
149 }
150 
151 /** Check the users password against the standard UNIX password table + PAM.
152  *
153  * @note For most flexibility, passing a pamauth type to this function
154  * allows you to have multiple authentication types (i.e. multiple
155  * files associated with radius in /etc/pam.d).
156  *
157  * @param request The current request.
158  * @param username User to authenticate.
159  * @param passwd Password to authenticate with,
160  * @param pamauth Type of PAM authentication.
161  * @return
162  * - 0 on success.
163  * - -1 on failure.
164  */
165 static int do_pam(request_t *request, char const *username, char const *passwd, char const *pamauth)
166 {
167  pam_handle_t *handle = NULL;
168  int ret;
169  rlm_pam_data_t pam_config;
170  struct pam_conv conv;
171 
172  /*
173  * Initialize the structures
174  */
175  conv.conv = pam_conv;
176  conv.appdata_ptr = &pam_config;
177  pam_config.request = request;
178  pam_config.username = username;
179  pam_config.password = passwd;
180  pam_config.error = false;
181 
182  RDEBUG2("Using pamauth string \"%s\" for pam.conf lookup", pamauth);
183 
184  ret = pam_start(pamauth, username, &conv, &handle);
185  if (ret != PAM_SUCCESS) {
186  RERROR("pam_start failed: %s", pam_strerror(handle, ret));
187  return -1;
188  }
189 
190  ret = pam_authenticate(handle, 0);
191  if (ret != PAM_SUCCESS) {
192  RERROR("pam_authenticate failed: %s", pam_strerror(handle, ret));
193  pam_end(handle, ret);
194  return -1;
195  }
196 
197  /*
198  * FreeBSD 3.x doesn't have account and session management
199  * functions in PAM, while 4.0 does.
200  */
201 #if !defined(__FreeBSD_version) || (__FreeBSD_version >= 400000)
202  ret = pam_acct_mgmt(handle, 0);
203  if (ret != PAM_SUCCESS) {
204  RERROR("pam_acct_mgmt failed: %s", pam_strerror(handle, ret));
205  pam_end(handle, ret);
206  return -1;
207  }
208 #endif
209  RDEBUG2("Authentication succeeded");
210  pam_end(handle, ret);
211  return 0;
212 }
213 
214 static unlang_action_t CC_HINT(nonnull) mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
215 {
217  int ret;
218  fr_pair_t *pair;
219 
220  char const *pam_auth_string = data->pam_auth_name;
221  fr_pair_t *username, *password;
222 
223  username = fr_pair_find_by_da(&request->request_pairs, NULL, attr_user_name);
224  password = fr_pair_find_by_da(&request->request_pairs, NULL, attr_user_password);
225 
226  /*
227  * We can only authenticate user requests which HAVE
228  * a User-Name attribute.
229  */
230  if (!username) {
231  REDEBUG("Attribute \"User-Name\" is required for authentication");
233  }
234 
235  if (!password) {
236  REDEBUG("Attribute \"User-Password\" is required for authentication");
238  }
239 
240  /*
241  * Make sure the supplied password isn't empty
242  */
243  if (password->vp_length == 0) {
244  REDEBUG("User-Password must not be empty");
246  }
247 
248  /*
249  * Log the password
250  */
251  if (RDEBUG_ENABLED3) {
252  RDEBUG("Login attempt with password \"%pV\"", &password->data);
253  } else {
254  RDEBUG2("Login attempt with password");
255  }
256 
257  /*
258  * Let control list over-ride the PAM auth name string,
259  * for backwards compatibility.
260  */
261  pair = fr_pair_find_by_da(&request->control_pairs, NULL, attr_pam_auth);
262  if (pair) pam_auth_string = pair->vp_strvalue;
263 
264  ret = do_pam(request, username->vp_strvalue, password->vp_strvalue, pam_auth_string);
265  if (ret < 0) RETURN_MODULE_REJECT;
266 
268 }
269 
270 extern module_rlm_t rlm_pam;
272  .common = {
273  .magic = MODULE_MAGIC_INIT,
274  .name = "pam",
275  .flags = MODULE_TYPE_THREAD_UNSAFE, /* The PAM libraries are not thread-safe */
276  .inst_size = sizeof(rlm_pam_t),
279  },
280  .method_group = {
281  .bindings = (module_method_binding_t[]){
282  { .section = SECTION_NAME("authenticate", CF_IDENT_ANY), .method = mod_authenticate },
284  }
285  }
286 };
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
log_entry msg
Definition: acutest.h:794
#define RCSID(id)
Definition: build.h:481
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:627
#define FR_CONF_OFFSET(_name, _struct, _field)
conf_parser_t which parses a single CONF_PAIR, writing the result to a field in a struct
Definition: cf_parse.h:268
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:564
#define CF_IDENT_ANY
Definition: cf_util.h:78
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:267
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:280
Specifies an attribute which must be present for the module to function.
Definition: dict.h:266
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:279
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:63
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition: log.h:335
#define RERROR(fmt,...)
Definition: log.h:298
talloc_free(reap)
main_config_t const * main_config
Main server configuration.
Definition: main_config.c:69
char const * name
Name of the daemon, usually 'radiusd'.
Definition: main_config.h:52
@ FR_TYPE_STRING
String of printable characters.
Definition: merged_model.c:83
module_instance_t const * mi
Instance of the module being instantiated.
Definition: module_ctx.h:42
module_instance_t * mi
Instance of the module being instantiated.
Definition: module_ctx.h:51
Temporary structure to hold arguments for module calls.
Definition: module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:50
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:39
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition: pair.c:693
static const conf_parser_t config[]
Definition: base.c:183
#define REDEBUG(fmt,...)
Definition: radclient.h:52
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
#define RDEBUG(fmt,...)
Definition: radclient.h:53
#define RETURN_MODULE_REJECT
Definition: rcode.h:55
#define RETURN_MODULE_INVALID
Definition: rcode.h:59
#define RETURN_MODULE_OK
Definition: rcode.h:57
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
static fr_dict_attr_t const * attr_user_password
Definition: rlm_pam.c:78
char const * password
Password to provide to PAM when prompted.
Definition: rlm_pam.c:57
fr_dict_autoload_t rlm_pam_dict[]
Definition: rlm_pam.c:70
fr_dict_attr_autoload_t rlm_pam_dict_attr[]
Definition: rlm_pam.c:81
static fr_dict_attr_t const * attr_pam_auth
Definition: rlm_pam.c:76
static fr_dict_t const * dict_freeradius
Definition: rlm_pam.c:66
static fr_dict_t const * dict_radius
Definition: rlm_pam.c:67
static unlang_action_t mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition: rlm_pam.c:214
static int pam_conv(int num_msg, struct pam_message const **msg, struct pam_response **resp, void *appdata_ptr)
Dialogue between RADIUS and PAM modules.
Definition: rlm_pam.c:102
bool error
True if pam_conv failed.
Definition: rlm_pam.c:58
char const * username
Username to provide to PAM when prompted.
Definition: rlm_pam.c:56
request_t * request
The current request.
Definition: rlm_pam.c:55
#define COPY_STRING(s)
char const * pam_auth_name
Definition: rlm_pam.c:51
static fr_dict_attr_t const * attr_user_name
Definition: rlm_pam.c:77
static int do_pam(request_t *request, char const *username, char const *passwd, char const *pamauth)
Check the users password against the standard UNIX password table + PAM.
Definition: rlm_pam.c:165
static const conf_parser_t module_config[]
Definition: rlm_pam.c:61
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_pam.c:88
module_rlm_t rlm_pam
Definition: rlm_pam.c:271
static int instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_rest.c:1302
username
Definition: rlm_securid.c:420
#define SECTION_NAME(_name1, _name2)
Define a section name consisting of a verb and a noun.
Definition: section.h:40
@ MODULE_TYPE_THREAD_UNSAFE
Module is not threadsafe.
Definition: module.h:48
void * data
Module's instance data.
Definition: module.h:271
#define MODULE_BINDING_TERMINATOR
Terminate a module binding list.
Definition: module.h:151
Named methods exported by a module.
Definition: module.h:173
return count
Definition: module.c:163
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
#define talloc_get_type_abort_const
Definition: talloc.h:282
static fr_slen_t data
Definition: value.h:1265
int nonnull(2, 5))