perl -i -npe "s/[ \t]+$//g" `find src -name "*.[ch]" -print`
[freeradius.git] / src / modules / rlm_pap / rlm_pap.c
1 /*
2  * rlm_pap.c
3  *
4  * Version:  $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  * Copyright 2001  The FreeRADIUS server project
21  * Copyright 2001  Kostas Kalevras <kkalev@noc.ntua.gr>
22  */
23
24 #include "autoconf.h"
25 #include "libradius.h"
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31
32 #include "radiusd.h"
33 #include "modules.h"
34 #include "conffile.h"
35 #include "../../include/md5.h"
36 #include "../../include/sha1.h"
37
38 #define PAP_ENC_INVALID -1
39 #define PAP_ENC_CLEAR           0
40 #define PAP_ENC_CRYPT           1
41 #define PAP_ENC_MD5             2
42 #define PAP_ENC_SHA1            3
43 #define PAP_MAX_ENC             3
44
45 #define PAP_INST_FREE(inst) \
46         free((char *)inst->scheme); \
47         free(inst)
48
49 static const char rcsid[] = "$Id$";
50
51 /*
52  *      Define a structure for our module configuration.
53  *
54  *      These variables do not need to be in a structure, but it's
55  *      a lot cleaner to do so, and a pointer to the structure can
56  *      be used as the instance handle.
57  */
58 typedef struct rlm_pap_t {
59         char *scheme;  /* password encryption scheme */
60         int sch;
61 } rlm_pap_t;
62
63 /*
64  *      A mapping of configuration file names to internal variables.
65  *
66  *      Note that the string is dynamically allocated, so it MUST
67  *      be freed.  When the configuration file parse re-reads the string,
68  *      it free's the old one, and strdup's the new one, placing the pointer
69  *      to the strdup'd string into 'config.string'.  This gets around
70  *      buffer over-flows.
71  */
72 static CONF_PARSER module_config[] = {
73   { "encryption_scheme", PW_TYPE_STRING_PTR, offsetof(rlm_pap_t,scheme), NULL, "crypt" },
74   { NULL, -1, 0, NULL, NULL }
75 };
76
77 static const char *pap_hextab = "0123456789abcdef";
78
79 /*
80  *  Smaller & faster than snprintf("%x");
81  *  Completely stolen from ns_mta_md5 module
82  */
83 static void pap_hexify(char *buffer, char *str, int len)
84 {
85         char *pch = str;
86         char ch;
87         int i;
88
89         for(i = 0;i < len; i ++) {
90                 ch = pch[i];
91                 buffer[2*i] = pap_hextab[(ch>>4) & 15];
92                 buffer[2*i + 1] = pap_hextab[ch & 15];
93         }
94         return;
95 }
96
97 static int pap_instantiate(CONF_SECTION *conf, void **instance)
98 {
99         rlm_pap_t *inst;
100
101         /*
102          *      Set up a storage area for instance data
103          */
104         inst = rad_malloc(sizeof(*inst));
105         if (!inst) {
106                 return -1;
107         }
108         memset(inst, 0, sizeof(*inst));
109
110         /*
111          *      If the configuration parameters can't be parsed, then
112          *      fail.
113          */
114         if (cf_section_parse(conf, inst, module_config) < 0) {
115                 free(inst);
116                 return -1;
117         }
118         inst->sch = PAP_ENC_INVALID;
119         if (inst->scheme == NULL || strlen(inst->scheme) == 0){
120                 radlog(L_ERR, "rlm_pap: Wrong password scheme passed");
121                 PAP_INST_FREE(inst);
122                 return -1;
123         }
124         if (strcasecmp(inst->scheme,"clear") == 0)
125                 inst->sch = PAP_ENC_CLEAR;
126         else if (strcasecmp(inst->scheme,"crypt") == 0){
127                 inst->sch = PAP_ENC_CRYPT;
128         }
129         else if (strcasecmp(inst->scheme,"md5") == 0)
130                 inst->sch = PAP_ENC_MD5;
131         else if (strcasecmp(inst->scheme,"sha1") == 0)
132                 inst->sch = PAP_ENC_SHA1;
133         else{
134                 radlog(L_ERR, "rlm_pap: Wrong password scheme passed");
135                 PAP_INST_FREE(inst);
136                 return -1;
137         }
138
139         *instance = inst;
140
141         return 0;
142 }
143
144 /*
145  *      Find the named user in this modules database.  Create the set
146  *      of attribute-value pairs to check and reply with for this user
147  *      from the database. The authentication code only needs to check
148  *      the password, the rest is done here.
149  */
150 static int pap_authenticate(void *instance, REQUEST *request)
151 {
152         VALUE_PAIR *passwd_item;
153         VALUE_PAIR *module_fmsg_vp;
154         char module_fmsg[MAX_STRING_LEN];
155         MD5_CTX md5_context;
156         SHA1_CTX sha1_context;
157         char digest[20];
158         char buff[MAX_STRING_LEN];
159         rlm_pap_t *inst = (rlm_pap_t *) instance;
160
161         /* quiet the compiler */
162         instance = instance;
163         request = request;
164
165         if(!request->username){
166                 radlog(L_AUTH, "rlm_pap: Attribute \"User-Name\" is required for authentication.\n");
167                 return RLM_MODULE_INVALID;
168         }
169
170         if (!request->password){
171                 radlog(L_AUTH, "rlm_pap: Attribute \"Password\" is required for authentication.");
172                 return RLM_MODULE_INVALID;
173         }
174
175         if (request->password->attribute != PW_PASSWORD) {
176                 radlog(L_AUTH, "rlm_pap: Attribute \"Password\" is required for authentication. Cannot use \"%s\".", request->password->name);
177                 return RLM_MODULE_INVALID;
178         }
179
180         if (request->password->length == 0) {
181                 radlog(L_ERR, "rlm_pap: empty password supplied");
182                 return RLM_MODULE_INVALID;
183         }
184
185         DEBUG("rlm_pap: login attempt by \"%s\" with password %s",
186                 request->username->strvalue, request->password->strvalue);
187
188         if (((passwd_item = pairfind(request->config_items, PW_PASSWORD)) == NULL) ||
189             (passwd_item->length == 0) || (passwd_item->strvalue[0] == 0)) {
190                 DEBUG("rlm_pap: No password (or empty password) to check against for for user %s",request->username->strvalue);
191                 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: User password not available");
192                 module_fmsg_vp = pairmake("Module-Failure-Message", module_fmsg, T_OP_EQ);
193                 pairadd(&request->packet->vps, module_fmsg_vp);
194                 return RLM_MODULE_INVALID;
195         }
196
197         DEBUG("rlm_pap: Using password \"%s\" for user %s authentication.",
198               passwd_item->strvalue, request->username->strvalue);
199
200         if (inst->sch == PAP_ENC_INVALID || inst->sch > PAP_MAX_ENC){
201                 radlog(L_ERR, "rlm_pap: Wrong password scheme");
202                 return RLM_MODULE_FAIL;
203         }
204         switch(inst->sch){
205                 default:
206                         radlog(L_ERR, "rlm_pap: Wrong password scheme");
207                         return RLM_MODULE_FAIL;
208                         break;
209                 case PAP_ENC_CLEAR:
210                         DEBUG("rlm_pap: Using clear text password.");
211                         if (strcmp((char *) passwd_item->strvalue,
212                                    (char *) request->password->strvalue) != 0){
213                                 DEBUG("rlm_pap: Passwords don't match");
214                                 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: CLEAR TEXT password check failed");
215                                 module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
216                                 pairadd(&request->packet->vps, module_fmsg_vp);
217                                 return RLM_MODULE_REJECT;
218                         }
219                         break;
220                 case PAP_ENC_CRYPT:
221                         DEBUG("rlm_pap: Using CRYPT encryption.");
222                         if (lrad_crypt_check((char *) request->password->strvalue,
223                                                                  (char *) passwd_item->strvalue) != 0) {
224                                 DEBUG("rlm_pap: Passwords don't match");
225                                 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: CRYPT password check failed");
226                                 module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
227                                 pairadd(&request->packet->vps, module_fmsg_vp);
228                                 return RLM_MODULE_REJECT;
229                         }
230                         break;
231                 case PAP_ENC_MD5:
232                         DEBUG("rlm_pap: Using MD5 encryption.");
233
234                         if (passwd_item->length != 32) {
235                                 DEBUG("rlm_pap: Configured MD5 password has incorrect length");
236                                 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured MD5 password has incorrect length");
237                                 module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
238                                 pairadd(&request->packet->vps, module_fmsg_vp);
239                                 return RLM_MODULE_REJECT;
240                         }
241
242                         MD5Init(&md5_context);
243                         MD5Update(&md5_context, request->password->strvalue, request->password->length);
244                         MD5Final(digest, &md5_context);
245                         pap_hexify(buff,digest,16);
246                         buff[32] = '\0';
247                         if (strcmp((char *)passwd_item->strvalue, buff) != 0){
248                                 DEBUG("rlm_pap: Passwords don't match");
249                                 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: MD5 password check failed");
250                                 module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
251                                 pairadd(&request->packet->vps, module_fmsg_vp);
252                                 return RLM_MODULE_REJECT;
253                         }
254                         break;
255                 case PAP_ENC_SHA1:
256
257                         DEBUG("rlm_pap: Using SHA1 encryption.");
258
259                         if (passwd_item->length != 40) {
260                                 DEBUG("rlm_pap: Configured SHA1 password has incorrect length");
261                                 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: Configured SHA1 password has incorrect length");
262                                 module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
263                                 pairadd(&request->packet->vps, module_fmsg_vp);
264                                 return RLM_MODULE_REJECT;
265                         }
266
267                         SHA1Init(&sha1_context);
268                         SHA1Update(&sha1_context, request->password->strvalue, request->password->length);
269                         SHA1Final(digest,&sha1_context);
270                         pap_hexify(buff,digest,20);
271                         buff[40] = '\0';
272                         if (strcmp((char *)passwd_item->strvalue, buff) != 0){
273                                 DEBUG("rlm_pap: Passwords don't match");
274                                 snprintf(module_fmsg,sizeof(module_fmsg),"rlm_pap: SHA1 password check failed");
275                                 module_fmsg_vp = pairmake("Module-Failure-Message",module_fmsg, T_OP_EQ);
276                                 pairadd(&request->packet->vps, module_fmsg_vp);
277                                 return RLM_MODULE_REJECT;
278                         }
279                         break;
280         }
281
282         DEBUG("rlm_pap: User authenticated succesfully");
283
284         return RLM_MODULE_OK;
285 }
286
287 static int pap_detach(void *instance)
288 {
289         rlm_pap_t *inst = (rlm_pap_t *) instance;
290
291         PAP_INST_FREE(inst);
292         return 0;
293 }
294
295
296 /*
297  *      The module name should be the only globally exported symbol.
298  *      That is, everything else should be 'static'.
299  *
300  *      If the module needs to temporarily modify it's instantiation
301  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
302  *      The server will then take care of ensuring that the module
303  *      is single-threaded.
304  */
305 module_t rlm_pap = {
306         "PAP",
307         0,                              /* type */
308         NULL,                           /* initialization */
309         pap_instantiate,                /* instantiation */
310         {
311                 pap_authenticate,       /* authentication */
312                 NULL,                   /* authorization */
313                 NULL,                   /* preaccounting */
314                 NULL,                   /* accounting */
315                 NULL,                   /* checksimul */
316                 NULL,                   /* pre-proxy */
317                 NULL,                   /* post-proxy */
318                 NULL                    /* post-auth */
319         },
320         pap_detach,                     /* detach */
321         NULL,                           /* destroy */
322 };