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