All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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: 44bd3ec1ae93180b7510b4ecd5a42745cd01eb37 $
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: 44bd3ec1ae93180b7510b4ecd5a42745cd01eb37 $")
32 
33 #include <freeradius-devel/radiusd.h>
34 #include <freeradius-devel/modules.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 rlm_pam_t {
51  char const *pam_auth_name;
52 } rlm_pam_t;
53 
54 static const CONF_PARSER module_config[] = {
55  { FR_CONF_OFFSET("pam_auth", PW_TYPE_STRING, rlm_pam_t, pam_auth_name) },
57 };
58 
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.
65 
66 static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
67 {
68  rlm_pam_t *inst = instance;
69 
70  if (!inst->pam_auth_name) inst->pam_auth_name = main_config.name;
71 
72  return 0;
73 }
74 
75 /** Dialogue between RADIUS and PAM modules
76  *
77  * Uses PAM's appdata_ptr so it's thread safe, and doesn't
78  * have any nasty static variables hanging around.
79  */
80 static int pam_conv(int num_msg, struct pam_message const **msg, struct pam_response **resp, void *appdata_ptr)
81 {
82  int count;
83  struct pam_response *reply;
84  REQUEST *request;
85  rlm_pam_data_t *pam_config = (rlm_pam_data_t *) appdata_ptr;
86 
87  request = pam_config->request;
88 
89 /* strdup(NULL) doesn't work on some platforms */
90 #define COPY_STRING(s) ((s) ? strdup(s) : NULL)
91 
92  reply = rad_malloc(num_msg * sizeof(struct pam_response));
93  memset(reply, 0, num_msg * sizeof(struct pam_response));
94  for (count = 0; count < num_msg; count++) {
95  switch (msg[count]->msg_style) {
96  case PAM_PROMPT_ECHO_ON:
97  reply[count].resp_retcode = PAM_SUCCESS;
98  reply[count].resp = COPY_STRING(pam_config->username);
99  break;
100 
101  case PAM_PROMPT_ECHO_OFF:
102  reply[count].resp_retcode = PAM_SUCCESS;
103  reply[count].resp = COPY_STRING(pam_config->password);
104  break;
105 
106  case PAM_TEXT_INFO:
107  RDEBUG2("%s", msg[count]->msg);
108  break;
109 
110  case PAM_ERROR_MSG:
111  default:
112  RERROR("PAM conversation failed");
113  /* Must be an error of some sort... */
114  for (count = 0; count < num_msg; count++) {
115  if (msg[count]->msg_style == PAM_ERROR_MSG) RERROR("%s", msg[count]->msg);
116  if (reply[count].resp) {
117  /* could be a password, let's be sanitary */
118  memset(reply[count].resp, 0, strlen(reply[count].resp));
119  free(reply[count].resp);
120  }
121  }
122  free(reply);
123  pam_config->error = true;
124  return PAM_CONV_ERR;
125  }
126  }
127  *resp = reply;
128  /* PAM frees reply (including reply[].resp) */
129 
130  return PAM_SUCCESS;
131 }
132 
133 /** Check the users password against the standard UNIX password table + PAM.
134  *
135  * @note For most flexibility, passing a pamauth type to this function
136  * allows you to have multiple authentication types (i.e. multiple
137  * files associated with radius in /etc/pam.d).
138  *
139  * @param request The current request.
140  * @param username User to authenticate.
141  * @param passwd Password to authenticate with,
142  * @param pamauth Type of PAM authentication.
143  * @return
144  * - 0 on success.
145  * - -1 on failure.
146  */
147 static int do_pam(REQUEST *request, char const *username, char const *passwd, char const *pamauth)
148 {
149  pam_handle_t *handle = NULL;
150  int ret;
151  rlm_pam_data_t pam_config;
152  struct pam_conv conv;
153 
154  /*
155  * Initialize the structures
156  */
157  conv.conv = pam_conv;
158  conv.appdata_ptr = &pam_config;
159  pam_config.request = request;
160  pam_config.username = username;
161  pam_config.password = passwd;
162  pam_config.error = false;
163 
164  RDEBUG2("Using pamauth string \"%s\" for pam.conf lookup", pamauth);
165 
166  ret = pam_start(pamauth, username, &conv, &handle);
167  if (ret != PAM_SUCCESS) {
168  RERROR("pam_start failed: %s", pam_strerror(handle, ret));
169  return -1;
170  }
171 
172  ret = pam_authenticate(handle, 0);
173  if (ret != PAM_SUCCESS) {
174  RERROR("pam_authenticate failed: %s", pam_strerror(handle, ret));
175  pam_end(handle, ret);
176  return -1;
177  }
178 
179  /*
180  * FreeBSD 3.x doesn't have account and session management
181  * functions in PAM, while 4.0 does.
182  */
183 #if !defined(__FreeBSD_version) || (__FreeBSD_version >= 400000)
184  ret = pam_acct_mgmt(handle, 0);
185  if (ret != PAM_SUCCESS) {
186  RERROR("pam_acct_mgmt failed: %s", pam_strerror(handle, ret));
187  pam_end(handle, ret);
188  return -1;
189  }
190 #endif
191  RDEBUG2("Authentication succeeded");
192  pam_end(handle, ret);
193  return 0;
194 }
195 
196 static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
197 {
198  int ret;
199  VALUE_PAIR *pair;
200  rlm_pam_t *data = (rlm_pam_t *) instance;
201 
202  char const *pam_auth_string = data->pam_auth_name;
203 
204  /*
205  * We can only authenticate user requests which HAVE
206  * a User-Name attribute.
207  */
208  if (!request->username) {
209  RAUTH("Attribute \"User-Name\" is required for authentication");
210  return RLM_MODULE_INVALID;
211  }
212 
213  /*
214  * We can only authenticate user requests which HAVE
215  * a User-Password attribute.
216  */
217  if (!request->password) {
218  RAUTH("Attribute \"User-Password\" is required for authentication");
219  return RLM_MODULE_INVALID;
220  }
221 
222  /*
223  * Ensure that we're being passed a plain-text password,
224  * and not anything else.
225  */
226  if (request->password->da->attr != PW_USER_PASSWORD) {
227  RAUTH("Attribute \"User-Password\" is required for authentication. Cannot use \"%s\".", request->password->da->name);
228  return RLM_MODULE_INVALID;
229  }
230 
231  /*
232  * Let the 'users' file over-ride the PAM auth name string,
233  * for backwards compatibility.
234  */
235  pair = fr_pair_find_by_num(request->config, 0, PW_PAM_AUTH, TAG_ANY);
236  if (pair) pam_auth_string = pair->vp_strvalue;
237 
238  ret = do_pam(request, request->username->vp_strvalue, request->password->vp_strvalue, pam_auth_string);
239  if (ret < 0) return RLM_MODULE_REJECT;
240 
241  return RLM_MODULE_OK;
242 }
243 
244 extern module_t rlm_pam;
245 module_t rlm_pam = {
247  .name = "pam",
248  .type = RLM_TYPE_THREAD_UNSAFE, /* The PAM libraries are not thread-safe */
249  .inst_size = sizeof(rlm_pam_t),
250  .config = module_config,
251  .instantiate = mod_instantiate,
252  .methods = {
254  },
255 };
256 
static const CONF_PARSER module_config[]
Definition: rlm_pam.c:54
#define RERROR(fmt,...)
Definition: log.h:207
Main server configuration.
Definition: radiusd.h:108
The module is OK, continue.
Definition: radiusd.h:91
Metadata exported by the module.
Definition: modules.h:134
static int do_pam(REQUEST *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:147
#define RLM_TYPE_THREAD_UNSAFE
Module is not threadsafe.
Definition: modules.h:76
void * rad_malloc(size_t size)
Definition: util.c:411
#define UNUSED
Definition: libradius.h:134
#define RLM_MODULE_INIT
Definition: modules.h:86
#define CONF_PARSER_TERMINATOR
Definition: conffile.h:289
char const * username
Username to provide to PAM when prompted.
Definition: rlm_pam.c:61
#define COPY_STRING(s)
#define inst
The module considers the request invalid.
Definition: radiusd.h:93
char const * name
Name of the daemon, usually 'radiusd'.
Definition: radiusd.h:109
static rlm_rcode_t mod_authenticate(void *instance, REQUEST *request) CC_HINT(nonnull)
static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance)
Definition: rlm_pam.c:66
Defines a CONF_PAIR to C data type mapping.
Definition: conffile.h:267
module_t rlm_pam
Definition: rlm_pam.c:245
Immediately reject the request.
Definition: radiusd.h:89
struct rlm_pam_t rlm_pam_t
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
0 methods index for authenticate section.
Definition: modules.h:41
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:80
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
static rs_t * conf
Definition: radsniff.c:46
#define RDEBUG2(fmt,...)
Definition: log.h:244
char const * password
Password to provide to PAM when prompted.
Definition: rlm_pam.c:62
uint8_t data[]
Definition: eap_pwd.h:625
uint64_t magic
Used to validate module struct.
Definition: modules.h:135
#define TAG_ANY
Definition: pair.h:191
#define FR_CONF_OFFSET(_n, _t, _s, _f)
Definition: conffile.h:168
#define RAUTH(fmt,...)
Definition: log.h:202
REQUEST * request
The current request.
Definition: rlm_pam.c:60
char const * pam_auth_name
Definition: rlm_pam.c:51
VALUE_PAIR * fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag)
Find the pair with the matching attribute.
Definition: pair.c:639
static rlm_rcode_t CC_HINT(nonnull)
Definition: rlm_pam.c:196
struct rlm_pam_data_t rlm_pam_data_t
String of printable characters.
Definition: radius.h:33
#define RCSID(id)
Definition: build.h:135
bool error
True if pam_conv failed.
Definition: rlm_pam.c:63