Mark parameters unused
[freeradius.git] / src / modules / rlm_dbm / rlm_dbm.c
1 /*
2  *   This program is is free software; you can redistribute it and/or modify
3  *   it under the terms of the GNU General Public License, version 2 if the
4  *   License as published by the Free Software Foundation.
5  *
6  *   This program is distributed in the hope that it will be useful,
7  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
8  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  *   GNU General Public License for more details.
10  *
11  *   You should have received a copy of the GNU General Public License
12  *   along with this program; if not, write to the Free Software
13  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
14  */
15  
16 /**
17  * $Id$
18  * @file rlm_dbm.c
19  * @brief Authorize using ndbm database
20  *
21  * @copyright 2001 Koulik Andrei, Sandy Service
22  * @copyright 2006 The FreeRADIUS server project
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, TAG_ANY);
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 * 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         memcpy(&k.dptr, &username, sizeof(k.dptr));
175         k.dsize = strlen(username) + 1 ; /* username stored with '\0' */
176
177         d = dbm_fetch(pdb, k);
178         if ( d.dptr == NULL ) {
179                  DEBUG2("rlm_dbm: User <%s> not found in database\n",username);
180                  return RLM_MODULE_NOTFOUND;
181         }
182
183         ch = d.dptr;
184         ch [ d.dsize - 1 ] = '\0'; /* should be closed by 0 */
185
186         DEBUG2("sm_parse_user: start parsing: user: %s", username);
187
188         /*  start parse content */
189         while ( parse_state != SMP_ERROR && *ch && continue_search ) {
190
191                 beg = ch;
192
193                 while( *ch && *ch != '\n') ch++ ;
194
195                 if ( *ch == '\n' ) { *ch = 0; ch++; }
196
197                 DEBUG2("parse buffer: <<%s>>\n",beg);
198
199                 retcod = userparse(beg,&vp);
200                 if ( retcod == T_OP_INVALID ) fr_perror("parse error ");
201
202                 switch ( retcod ) {
203                         case T_COMMA: break; /* continue parse the current list */
204                         case T_EOL:     DEBUG2("rlm_dbm: recod parsed\n"); /* vp contains full pair list */
205                                         if ( parse_state == SMP_PATTERN ) { /* pattern line found */
206                                                 DEBUG2("process pattern");
207                                                 /* check pattern against request */
208                                                 if ( paircompare(req, request, vp, reply ) == 0 ) {
209                                                         DEBUG2("rlm_dbm: Pattern matched, look for request");
210                                                         pairmove(&tmp_config, &vp);
211                                                         pairfree(&vp);
212                                                         parse_state = SMP_REPLY; /* look for reply */
213                                                 } else  {
214                                                           /* skip reply */
215                                                         DEBUG2("rlm_dbm: patern not matched, reply skiped");
216                                                         pairfree(&vp);
217                                                         while ( *ch && *ch !='\n' ) ch++;
218                                                         if ( *ch == '\n' ) ch++;
219                                                 }
220                                         } else { /* reply line found */
221                                                 /* look for join-attribute */
222                                                 DEBUG2("rlm_dbm: Reply found");
223                                                 join_attr = vp;
224                                                 while( (join_attr = pairfind(join_attr, SM_JOIN_ATTR, 0, TAG_ANY) ) != NULL ) {
225                                                         DEBUG2("rlm_dbm: Proccess nested record: username %s",
226                                                                 (char *)join_attr->vp_strvalue);
227                                                         /* res =  RLM_MODULE_NOTFOUND; */
228                                                         res =  sm_parse_user(pdb, (char *)join_attr->vp_strvalue, req, request, &tmp_config,
229                                                                         &nu_reply, ulist);
230                                                         DEBUG("rlm_dbm: recived: %d\n",res);
231                                                         switch ( res ) {
232                                                                 case RLM_MODULE_NOTFOUND:
233                                                                 case RLM_MODULE_OK:
234                                                                         break;
235                                                                 default: /* seems error code */
236                                                                         parse_state = SMP_ERROR;
237                                                                         DEBUG2("rlm_dbm: Nested record error\n");
238                                                                         break;
239                                                         }
240                                                         join_attr = join_attr -> next;
241                                                 }
242                                                 pairdelete(&vp,SM_JOIN_ATTR, 0, TAG_ANY);
243                                                 if ( parse_state != SMP_ERROR ) {
244                                                         if ( ! isfallthrough(vp) ) {
245                                                           continue_search = 0;
246                                                           DEBUG2("rlm_dbm: Break search due Fall-Through = no");
247                                                         }
248                                                         pairmove(&vp,&nu_reply);
249                                                         pairfree(&nu_reply);
250                                                         pairmove(&tmp_reply,&vp);
251                                                         pairfree(&vp);
252                                                         parse_state = SMP_PATTERN;
253                                                         found = RLM_MODULE_OK;
254                                                 }
255                                                 pairfree(&vp);
256                                                 pairfree(&nu_reply);                                    }
257                                         break;
258                         default:        /* we do not wait that !!!! */
259                                         parse_state = SMP_ERROR;
260                                         DEBUG2("rlm_dbm: Unknown token: %d\n",retcod);
261                                         break;
262                 }
263
264         }
265         if ( parse_state == SMP_PATTERN  ) {
266                 pairmove(config,&tmp_config);
267                 pairfree(&tmp_config);
268                 pairmove(reply,&tmp_reply);
269                 pairfree(&tmp_reply);
270         } else {
271                 pairfree(&tmp_config);
272                 pairfree(&tmp_reply);
273                 pairfree(&vp);
274                 DEBUG2("rlm_dbm: Bad final parse state: %d\n",parse_state);
275                 found = RLM_MODULE_FAIL ;
276         }
277         pairfree(&vp);
278         return found;
279 }
280
281 static int sm_postprocessor(VALUE_PAIR **reply UNUSED) {
282         return 0;
283 }
284
285 static int rlm_dbm_instantiate(CONF_SECTION *conf, void **instance)
286 {
287         struct rlm_dbm_t *inst;
288
289         *instance = inst = talloc_zero(conf, struct rlm_dbm_t);
290         if (!inst) return -1;
291
292         if (cf_section_parse(conf, inst, module_config) < 0) {
293                 return -1;
294         }
295
296         return 0;
297 }
298 static rlm_rcode_t rlm_dbm_authorize(void *instance, REQUEST *request)
299 {
300         VALUE_PAIR      *namepair;
301         VALUE_PAIR      *request_pairs;
302         VALUE_PAIR      *check_tmp = NULL;
303         VALUE_PAIR      *reply_tmp = NULL;
304
305         int             found = 0;
306         const char      *name;
307         SM_USER_ENTRY   *ulist = NULL;
308         DBM             *pdb;
309
310         struct rlm_dbm_t *inst = instance;
311
312         VALUE_PAIR **check_pairs, **reply_pairs;
313
314         request_pairs = request->packet->vps;
315         check_pairs = &request->config_items;
316         reply_pairs = &request->reply->vps;
317
318         /*
319          *      Grab the canonical user name.
320          */
321         namepair = request->username;
322         name = namepair ? (char *) namepair->vp_strvalue : "NONE";
323
324         DEBUG2("rlm_dbm: try open database file: %s\n",inst -> userfile);
325
326         /* open database */
327         if ( ( pdb = dbm_open(inst->userfile, O_RDONLY, 0600) ) != NULL ) {
328                 DEBUG("rlm_dbm: Call parse_user:\n");
329                 found = sm_parse_user(pdb, name, request, request_pairs, &check_tmp, &reply_tmp, &ulist);
330                 if ( found == RLM_MODULE_NOTFOUND ) {
331                   sm_user_list_wipe(&ulist);
332                   found = sm_parse_user(pdb, "DEFAULT", request, request_pairs, &check_tmp, &reply_tmp, &ulist);
333                 }
334                 dbm_close(pdb);
335         } else {
336                 found = RLM_MODULE_FAIL;
337                 DEBUG2("rlm_dbm: Cannot open database file: %s\n",
338                        strerror(errno));
339         }
340
341         if ( found == RLM_MODULE_OK ) {
342                 /* do preprocessor for final reply-pair tranformation */
343                 if ( !sm_postprocessor(&reply_tmp) ) {
344                         pairmove(reply_pairs, &reply_tmp);
345                         pairmove(check_pairs, &check_tmp);
346                 } else found = RLM_MODULE_FAIL;
347         }
348         sm_user_list_wipe(&ulist);
349         pairfree(&reply_tmp);
350         pairfree(&check_tmp);
351
352         return found;
353 }
354
355 /* globally exported name */
356 module_t rlm_dbm = {
357         RLM_MODULE_INIT,
358         "dbm",
359         0,                              /* type: reserved */
360         rlm_dbm_instantiate,            /* instantiation */
361         NULL,                            /* detach */
362         {
363                 NULL,                   /* authentication */
364                 rlm_dbm_authorize,      /* authorization */
365                 NULL,                   /* preaccounting */
366                 NULL,                   /* accounting */
367                 NULL,                    /* checksimul */
368                 NULL,                   /* pre-proxy */
369                 NULL,                   /* post-proxy */
370                 NULL                    /* post-auth */
371         },
372 };