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