Auto-generate headers from dictionarty.freeradius.internal
[freeradius.git] / src / modules / rlm_pam / rlm_pam.c
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, version 2 if the
4  *   License as published by the Free Software Foundation.
5  *
6  *   This program is distributed in the hope that it will be useful,
7  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
8  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  *   GNU General Public License for more details.
10  *
11  *   You should have received a copy of the GNU General Public License
12  *   along with this program; if not, write to the Free Software
13  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
14  */
15
16 /**
17  * $Id$
18  * @file rlm_pam.c
19  * @brief Interfaces with the PAM library to allow auth via PAM.
20  *
21  * @note This was taken from the hacks that miguel a.l. paraz <map@iphil.net>
22  * @note did on radiusd-cistron-1.5.3 and migrated to a separate file.
23  * @note That, in fact, was again based on the original stuff from
24  * @note Jeph Blaize <jblaize@kiva.net> done in May 1997.
25  *
26  * @copyright 2000,2006  The FreeRADIUS server project
27  * @copyright 1997  Jeph Blaize <jblaize@kiva.net>
28  * @copyright 1999  miguel a.l. paraz <map@iphil.net>
29  */
30 RCSID("$Id$")
31
32 #include        <freeradius-devel/radiusd.h>
33 #include        <freeradius-devel/modules.h>
34
35 #include        "config.h"
36
37 #ifdef HAVE_SECURITY_PAM_APPL_H
38 #include        <security/pam_appl.h>
39 #endif
40
41 #ifdef HAVE_PAM_PAM_APPL_H
42 #include        <pam/pam_appl.h>
43 #endif
44
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         { "pam_auth", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_pam_t, pam_auth_name), "radiusd" },
56         { NULL, -1, 0, NULL, NULL }
57 };
58
59 /*************************************************************************
60  *
61  *      Function: PAM_conv
62  *
63  *      Purpose: Dialogue between RADIUS and PAM modules.
64  *
65  * jab - stolen from pop3d
66  *
67  * Alan DeKok: modified to use PAM's appdata_ptr, so that we're
68  *           multi-threaded safe, and don't have any nasty static
69  *           variables hanging around.
70  *
71  *************************************************************************/
72
73 typedef struct my_PAM {
74   char const *username;
75   char const *password;
76   int    error;
77 } my_PAM;
78
79 static int PAM_conv(int num_msg, struct pam_message const **msg, struct pam_response **resp, void *appdata_ptr) {
80   int count;
81   struct pam_response *reply;
82   my_PAM *pam_config = (my_PAM *) appdata_ptr;
83
84 /* strdup(NULL) doesn't work on some platforms */
85 #define COPY_STRING(s) ((s) ? strdup(s) : NULL)
86
87   reply = rad_malloc(num_msg * sizeof(struct pam_response));
88   memset(reply, 0, num_msg * sizeof(struct pam_response));
89   for (count = 0; count < num_msg; count++) {
90     switch (msg[count]->msg_style) {
91     case PAM_PROMPT_ECHO_ON:
92       reply[count].resp_retcode = PAM_SUCCESS;
93       reply[count].resp = COPY_STRING(pam_config->username);
94       break;
95     case PAM_PROMPT_ECHO_OFF:
96       reply[count].resp_retcode = PAM_SUCCESS;
97       reply[count].resp = COPY_STRING(pam_config->password);
98       break;
99     case PAM_TEXT_INFO:
100       /* ignore it... */
101       break;
102     case PAM_ERROR_MSG:
103     default:
104       /* Must be an error of some sort... */
105       for (count = 0; count < num_msg; count++) {
106         if (reply[count].resp) {
107           /* could be a password, let's be sanitary */
108           memset(reply[count].resp, 0, strlen(reply[count].resp));
109           free(reply[count].resp);
110         }
111       }
112       free(reply);
113       pam_config->error = 1;
114       return PAM_CONV_ERR;
115     }
116   }
117   *resp = reply;
118   /* PAM frees reply (including reply[].resp) */
119
120   return PAM_SUCCESS;
121 }
122
123 /*************************************************************************
124  *
125  *      Function: pam_pass
126  *
127  *      Purpose: Check the users password against the standard UNIX
128  *               password table + PAM.
129  *
130  * jab start 19970529
131  *************************************************************************/
132
133 /* cjd 19980706
134  *
135  * 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 static int pam_pass(char const *name, char const *passwd, char const *pamauth)
140 {
141     pam_handle_t *pamh=NULL;
142     int retval;
143     my_PAM pam_config;
144     struct pam_conv conv;
145
146     /*
147      *  Initialize the structures.
148      */
149     conv.conv = PAM_conv;
150     conv.appdata_ptr = &pam_config;
151     pam_config.username = name;
152     pam_config.password = passwd;
153     pam_config.error = 0;
154
155     DEBUG("pam_pass: using pamauth string <%s> for pam.conf lookup", pamauth);
156     retval = pam_start(pamauth, name, &conv, &pamh);
157     if (retval != PAM_SUCCESS) {
158       DEBUG("pam_pass: function pam_start FAILED for <%s>. Reason: %s",
159             name, pam_strerror(pamh, retval));
160       return -1;
161     }
162
163     retval = pam_authenticate(pamh, 0);
164     if (retval != PAM_SUCCESS) {
165       DEBUG("pam_pass: function pam_authenticate FAILED for <%s>. Reason: %s",
166             name, pam_strerror(pamh, retval));
167       pam_end(pamh, retval);
168       return -1;
169     }
170
171     /*
172      * FreeBSD 3.x doesn't have account and session management
173      * functions in PAM, while 4.0 does.
174      */
175 #if !defined(__FreeBSD_version) || (__FreeBSD_version >= 400000)
176     retval = pam_acct_mgmt(pamh, 0);
177     if (retval != PAM_SUCCESS) {
178       DEBUG("pam_pass: function pam_acct_mgmt FAILED for <%s>. Reason: %s",
179             name, pam_strerror(pamh, retval));
180       pam_end(pamh, retval);
181       return -1;
182     }
183 #endif
184
185     DEBUG("pam_pass: authentication succeeded for <%s>", name);
186     pam_end(pamh, retval);
187     return 0;
188 }
189
190 /* translate between function declarations */
191 static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request)
192 {
193         int     r;
194         VALUE_PAIR *pair;
195         rlm_pam_t *data = (rlm_pam_t *) instance;
196
197         char const *pam_auth_string = data->pam_auth_name;
198
199         /*
200          *      We can only authenticate user requests which HAVE
201          *      a User-Name attribute.
202          */
203         if (!request->username) {
204                 AUTH("rlm_pam: Attribute \"User-Name\" is required for authentication");
205                 return RLM_MODULE_INVALID;
206         }
207
208         /*
209          *      We can only authenticate user requests which HAVE
210          *      a User-Password attribute.
211          */
212         if (!request->password) {
213                 AUTH("rlm_pam: Attribute \"User-Password\" is required for authentication");
214                 return RLM_MODULE_INVALID;
215         }
216
217         /*
218          *  Ensure that we're being passed a plain-text password,
219          *  and not anything else.
220          */
221         if (request->password->da->attr != PW_USER_PASSWORD) {
222                 AUTH("rlm_pam: Attribute \"User-Password\" is required for authentication.  Cannot use \"%s\".", request->password->da->name);
223                 return RLM_MODULE_INVALID;
224         }
225
226         /*
227          *      Let the 'users' file over-ride the PAM auth name string,
228          *      for backwards compatibility.
229          */
230         pair = pairfind(request->config_items, PW_PAM_AUTH, 0, TAG_ANY);
231         if (pair) pam_auth_string = pair->vp_strvalue;
232
233         r = pam_pass(request->username->vp_strvalue,
234                      request->password->vp_strvalue,
235                      pam_auth_string);
236
237         if (r == 0) {
238                 return RLM_MODULE_OK;
239         }
240         return RLM_MODULE_REJECT;
241 }
242
243 module_t rlm_pam = {
244         RLM_MODULE_INIT,
245         "pam",
246         RLM_TYPE_THREAD_UNSAFE, /* The PAM libraries are not thread-safe */
247         sizeof(rlm_pam_t),
248         module_config,
249         NULL,                           /* instantiation */
250         NULL,                           /* detach */
251         {
252                 mod_authenticate,       /* authenticate */
253                 NULL,                   /* authorize */
254                 NULL,                   /* pre-accounting */
255                 NULL,                   /* accounting */
256                 NULL,                   /* checksimul */
257                 NULL,                   /* pre-proxy */
258                 NULL,                   /* post-proxy */
259                 NULL                    /* post-auth */
260         },
261 };
262