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