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