Automatic search and replace for pairfind.
[freeradius.git] / src / modules / rlm_dbm / rlm_dbm.c
1 /*
2  * rlm_dbm.c authorize:    authorize using ndbm database
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2001 Koulik Andrei, Sandy Service
21  * Copyright 2006 The FreeRADIUS server project
22  */
23
24 #include <freeradius-devel/ident.h>
25 RCSID("$Id$")
26
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
29
30 #ifdef HAVE_NDBM_H
31 #include <ndbm.h>
32 #endif
33
34 #ifdef HAVE_GDBM_NDBM_H
35 #include <gdbm/ndbm.h>
36 #endif
37
38 #ifdef HAVE_GDBMNDBM_H
39 #include <gdbm-ndbm.h>
40 #endif
41
42 #include <fcntl.h>
43
44 #ifdef SANDY_MOD
45 #       include "sandymod.h"
46 #endif
47
48 #define MYDBM   DBM
49 #define get_user_content dbm_fetch
50
51 #define SM_JOIN_ATTR    1029
52
53 #ifdef SANDY_MOD
54 #       define SM_POOL_ATTR     510
55 #endif
56
57 typedef struct rlm_dbm_t {
58
59 #ifdef SANDY_MOD
60         char    *dms_servers;
61         char    *ducpd_servers;
62 #endif
63         char    *userfile;
64         int     findmod;
65 } rlm_dbm_t;
66
67 typedef struct user_entry {
68         char * username;
69         struct user_entry * next;
70 } SM_USER_ENTRY;
71
72
73 static const CONF_PARSER module_config[] = {
74         { "usersfile",     PW_TYPE_STRING_PTR,offsetof(struct rlm_dbm_t,userfile),
75                 NULL, "/etc/uf" },
76         { NULL, -1, 0, NULL, NULL }
77 };
78
79 static void sm_user_list_wipe (SM_USER_ENTRY **ue) {
80
81         SM_USER_ENTRY * pue, *nue;
82
83         if ( ! *ue ) return ;
84         pue = *ue;
85
86         while ( pue != NULL ) {
87                 nue = pue -> next;
88                 DEBUG2("Remove %s from user list", pue -> username);
89                 free(pue -> username);
90                 free(pue);
91                 pue = nue;
92         }
93         *ue = NULL;
94 }
95
96 /*
97  *      add username un to user list ue;
98  *      return 0 if user succefuly added
99  *      1 - if username already exists
100  *      -1 - error: no memmory
101  */
102
103 static int sm_user_list_add(SM_USER_ENTRY **ue, const char *un) {
104
105         while( *ue ) {
106                 if ( strcmp( (*ue) -> username, un) == 0 ) return 1;
107                 ue = & ((*ue) -> next);
108         }
109         *ue = malloc(sizeof(SM_USER_ENTRY));
110         if ( !*ue ) return -1;
111         (*ue)  -> username = strdup(un);
112         DEBUG2("Add %s to user list", (*ue) -> username);
113         (*ue)  -> next = NULL ;
114         if ( ! (*ue) -> username ) {
115                 free(*ue);
116                 *ue = NULL;
117                 return -1;
118         } else return 0;
119 }
120
121
122 enum {
123    SMP_PATTERN,
124    SMP_REPLY,
125    SMP_ERROR
126 };
127
128
129 /******/
130
131 static int isfallthrough(VALUE_PAIR *vp) {
132   VALUE_PAIR * tmp;
133
134   tmp = pairfind(vp, PW_FALL_THROUGH, 0);
135   return tmp ? tmp -> vp_integer : 1; /* if no  FALL_THROUGH - keep looking */
136 }
137
138 /* sm_parse_user
139  *  find user, parse and return result
140  * in-parameters:
141  *  pdb - ndbm handler
142  *  username - user name from request
143  *  request - pair originated from the nas
144  *  mode - search mode SM_SM_ACCUM - accumulative search mode
145  *  out-parameters:
146  *  in-out:
147  *  parsed_users - list of parsed user names for loop removal
148  */
149
150 static int sm_parse_user(DBM *pdb, const char * username, REQUEST *req,
151                          VALUE_PAIR const* request, VALUE_PAIR **config,
152                          VALUE_PAIR **reply, SM_USER_ENTRY **ulist)
153 {
154         datum   k,d;
155         int             retcod, found = RLM_MODULE_NOTFOUND, res ;
156         VALUE_PAIR *vp = NULL,* tmp_config = NULL, *tmp_reply = NULL, *nu_reply = NULL;
157         VALUE_PAIR *join_attr;
158         char    *ch,*beg;
159
160         int     parse_state = SMP_PATTERN;
161         int     continue_search = 1;
162
163         /* check for loop */
164
165         DEBUG2("sm_parse_user.c: check for loops");
166
167         if ( (retcod = sm_user_list_add(ulist,username) ) ) {
168                 if ( retcod < 0 ) radlog(L_ERR,"rlm_dbm: Couldn't allocate memory");
169                         else radlog(L_ERR,"rlm_dbm: Invalid configuration: loop detected");
170                 return RLM_MODULE_FAIL;
171         }
172
173         /* retrieve user content */
174
175         k.dptr  = username;
176         k.dsize = strlen(username) + 1 ; /* username stored with '\0' */
177
178         d = dbm_fetch(pdb, k);
179         if ( d.dptr == NULL ) {
180                  DEBUG2("rlm_dbm: User <%s> not found in database\n",username);
181                  return RLM_MODULE_NOTFOUND;
182         }
183
184         ch = d.dptr;
185         ch [ d.dsize - 1 ] = '\0'; /* should be closed by 0 */
186
187         DEBUG2("sm_parse_user: start parsing: user: %s", username);
188
189         /*  start parse content */
190         while ( parse_state != SMP_ERROR && *ch && continue_search ) {
191
192                 beg = ch;
193
194                 while( *ch && *ch != '\n') ch++ ;
195
196                 if ( *ch == '\n' ) { *ch = 0; ch++; }
197
198                 DEBUG2("parse buffer: <<%s>>\n",beg);
199
200                 retcod = userparse(beg,&vp);
201                 if ( retcod == T_OP_INVALID ) fr_perror("parse error ");
202
203                 switch ( retcod ) {
204                         case T_COMMA: break; /* continue parse the current list */
205                         case T_EOL:     DEBUG2("rlm_dbm: recod parsed\n"); /* vp contains full pair list */
206                                         if ( parse_state == SMP_PATTERN ) { /* pattern line found */
207                                                 DEBUG2("process pattern");
208                                                 /* check pattern against request */
209                                                 if ( paircompare(req, request, vp, reply ) == 0 ) {
210                                                         DEBUG2("rlm_dbm: Pattern matched, look for request");
211                                                         pairmove(&tmp_config, &vp);
212                                                         pairfree(&vp);
213                                                         parse_state = SMP_REPLY; /* look for reply */
214                                                 } else  {
215                                                           /* skip reply */
216                                                         DEBUG2("rlm_dbm: patern not matched, reply skiped");
217                                                         pairfree(&vp);
218                                                         while ( *ch && *ch !='\n' ) ch++;
219                                                         if ( *ch == '\n' ) ch++;
220                                                 }
221                                         } else { /* reply line found */
222                                                 /* look for join-attribute */
223                                                 DEBUG2("rlm_dbm: Reply found");
224                                                 join_attr = vp;
225                                                 while( (join_attr = pairfind(join_attr,SM_JOIN_ATTR) ) != NULL ) {
226                                                         DEBUG2("rlm_dbm: Proccess nested record: username %s",
227                                                                 (char *)join_attr->vp_strvalue);
228                                                         /* res =  RLM_MODULE_NOTFOUND; */
229                                                         res =  sm_parse_user(pdb, (char *)join_attr->vp_strvalue, req, request, &tmp_config,
230                                                                         &nu_reply, ulist);
231                                                         DEBUG("rlm_dbm: recived: %d\n",res);
232                                                         switch ( res ) {
233                                                                 case RLM_MODULE_NOTFOUND:
234                                                                 case RLM_MODULE_OK:
235                                                                         break;
236                                                                 default: /* seems error code */
237                                                                         parse_state = SMP_ERROR;
238                                                                         DEBUG2("rlm_dbm: Nested record error\n");
239                                                                         break;
240                                                         }
241                                                         join_attr = join_attr -> next;
242                                                 }
243                                                 pairdelete(&vp,SM_JOIN_ATTR);
244                                                 if ( parse_state != SMP_ERROR ) {
245                                                         if ( ! isfallthrough(vp) ) {
246                                                           continue_search = 0;
247                                                           DEBUG2("rlm_dbm: Break search due Fall-Through = no");
248                                                         }
249                                                         pairmove(&vp,&nu_reply);
250                                                         pairfree(&nu_reply);
251                                                         pairmove(&tmp_reply,&vp);
252                                                         pairfree(&vp);
253                                                         parse_state = SMP_PATTERN;
254                                                         found = RLM_MODULE_OK;
255                                                 }
256                                                 pairfree(&vp);
257                                                 pairfree(&nu_reply);                                    }
258                                         break;
259                         default:        /* we do not wait that !!!! */
260                                         parse_state = SMP_ERROR;
261                                         DEBUG2("rlm_dbm: Unknown token: %d\n",retcod);
262                                         break;
263                 }
264
265         }
266         if ( parse_state == SMP_PATTERN  ) {
267                 pairmove(config,&tmp_config);
268                 pairfree(&tmp_config);
269                 pairmove(reply,&tmp_reply);
270                 pairfree(&tmp_reply);
271         } else {
272                 pairfree(&tmp_config);
273                 pairfree(&tmp_reply);
274                 pairfree(&vp);
275                 DEBUG2("rlm_dbm: Bad final parse state: %d\n",parse_state);
276                 found = RLM_MODULE_FAIL ;
277         }
278         pairfree(&vp);
279         return found;
280 }
281
282 static int sm_postprocessor(VALUE_PAIR **reply UNUSED) {
283         return 0;
284 }
285
286 static int rlm_dbm_instantiate(CONF_SECTION *conf, void **instance) {
287         struct rlm_dbm_t *inst;
288
289         inst = rad_malloc(sizeof(rlm_dbm_t));
290         if (!inst) {
291                 return -1;
292         }
293         memset(inst, 0, sizeof(*inst));
294
295         if (cf_section_parse(conf, inst, module_config) < 0) {
296                 free(inst);
297                 return -1;
298         }
299         *instance = inst;
300         return 0;
301 }
302 static int rlm_dbm_authorize(void *instance, REQUEST *request)
303 {
304         VALUE_PAIR      *namepair;
305         VALUE_PAIR      *request_pairs;
306         VALUE_PAIR      *check_tmp = NULL;
307         VALUE_PAIR      *reply_tmp = NULL;
308
309         int             found = 0;
310         const char      *name;
311         SM_USER_ENTRY   *ulist = NULL;
312         DBM             *pdb;
313
314         struct rlm_dbm_t *inst = instance;
315
316         VALUE_PAIR **check_pairs, **reply_pairs;
317
318         request_pairs = request->packet->vps;
319         check_pairs = &request->config_items;
320         reply_pairs = &request->reply->vps;
321
322         /*
323          *      Grab the canonical user name.
324          */
325         namepair = request->username;
326         name = namepair ? (char *) namepair->vp_strvalue : "NONE";
327
328         DEBUG2("rlm_dbm: try open database file: %s\n",inst -> userfile);
329
330         /* open database */
331         if ( ( pdb = dbm_open(inst->userfile, O_RDONLY, 0600) ) != NULL ) {
332                 DEBUG("rlm_dbm: Call parse_user:\n");
333                 found = sm_parse_user(pdb, name, request, request_pairs, &check_tmp, &reply_tmp, &ulist);
334                 if ( found == RLM_MODULE_NOTFOUND ) {
335                   sm_user_list_wipe(&ulist);
336                   found = sm_parse_user(pdb, "DEFAULT", request, request_pairs, &check_tmp, &reply_tmp, &ulist);
337                 }
338                 dbm_close(pdb);
339         } else {
340                 found = RLM_MODULE_FAIL;
341                 DEBUG2("rlm_dbm: Cannot open database file: %s\n",
342                        strerror(errno));
343         }
344
345         if ( found == RLM_MODULE_OK ) {
346                 /* do preprocessor for final reply-pair tranformation */
347                 if ( !sm_postprocessor(&reply_tmp) ) {
348                         pairmove(reply_pairs, &reply_tmp);
349                         pairmove(check_pairs, &check_tmp);
350                 } else found = RLM_MODULE_FAIL;
351         }
352         sm_user_list_wipe(&ulist);
353         pairfree(&reply_tmp);
354         pairfree(&check_tmp);
355
356         return found;
357 }
358
359 static int rlm_dbm_detach(void *instance)
360 {
361         struct rlm_dbm_t *inst = instance;
362         free(inst);
363         return 0;
364 }
365
366
367 /* globally exported name */
368 module_t rlm_dbm = {
369         RLM_MODULE_INIT,
370         "dbm",
371         0,                              /* type: reserved */
372         rlm_dbm_instantiate,            /* instantiation */
373         rlm_dbm_detach,                 /* detach */
374         {
375                 NULL,                   /* authentication */
376                 rlm_dbm_authorize,      /* authorization */
377                 NULL,                   /* preaccounting */
378                 NULL,                   /* accounting */
379                 NULL,                    /* checksimul */
380                 NULL,                   /* pre-proxy */
381                 NULL,                   /* post-proxy */
382                 NULL                    /* post-auth */
383         },
384 };