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.
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.
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
19 * @brief Authorize using ndbm database
21 * @copyright 2001 Koulik Andrei, Sandy Service
22 * @copyright 2006 The FreeRADIUS server project
24 #include <freeradius-devel/ident.h>
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/modules.h>
34 #ifdef HAVE_GDBM_NDBM_H
35 #include <gdbm/ndbm.h>
38 #ifdef HAVE_GDBMNDBM_H
39 #include <gdbm-ndbm.h>
45 # include "sandymod.h"
49 #define get_user_content dbm_fetch
51 #define SM_JOIN_ATTR 1029
54 # define SM_POOL_ATTR 510
57 typedef struct rlm_dbm_t {
67 typedef struct user_entry {
69 struct user_entry * next;
73 static const CONF_PARSER module_config[] = {
74 { "usersfile", PW_TYPE_STRING_PTR,offsetof(struct rlm_dbm_t,userfile),
76 { NULL, -1, 0, NULL, NULL }
79 static void sm_user_list_wipe (SM_USER_ENTRY **ue) {
81 SM_USER_ENTRY * pue, *nue;
86 while ( pue != NULL ) {
88 DEBUG2("Remove %s from user list", pue -> username);
89 free(pue -> username);
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
103 static int sm_user_list_add(SM_USER_ENTRY **ue, const char *un) {
106 if ( strcmp( (*ue) -> username, un) == 0 ) return 1;
107 ue = & ((*ue) -> next);
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 ) {
131 static int isfallthrough(VALUE_PAIR *vp) {
134 tmp = pairfind(vp, PW_FALL_THROUGH, 0, TAG_ANY);
135 return tmp ? tmp -> vp_integer : 1; /* if no FALL_THROUGH - keep looking */
139 * find user, parse and return result
142 * username - user name from request
143 * request - pair originated from the nas
144 * mode - search mode SM_SM_ACCUM - accumulative search mode
147 * parsed_users - list of parsed user names for loop removal
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)
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;
160 int parse_state = SMP_PATTERN;
161 int continue_search = 1;
165 DEBUG2("sm_parse_user.c: check for loops");
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;
173 /* retrieve user content */
174 memcpy(&k.dptr, &username, sizeof(k.dptr));
175 k.dsize = strlen(username) + 1 ; /* username stored with '\0' */
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;
184 ch [ d.dsize - 1 ] = '\0'; /* should be closed by 0 */
186 DEBUG2("sm_parse_user: start parsing: user: %s", username);
188 /* start parse content */
189 while ( parse_state != SMP_ERROR && *ch && continue_search ) {
193 while( *ch && *ch != '\n') ch++ ;
195 if ( *ch == '\n' ) { *ch = 0; ch++; }
197 DEBUG2("parse buffer: <<%s>>\n",beg);
199 retcod = userparse(beg,&vp);
200 if ( retcod == T_OP_INVALID ) fr_perror("parse error ");
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);
212 parse_state = SMP_REPLY; /* look for reply */
215 DEBUG2("rlm_dbm: patern not matched, reply skiped");
217 while ( *ch && *ch !='\n' ) ch++;
218 if ( *ch == '\n' ) ch++;
220 } else { /* reply line found */
221 /* look for join-attribute */
222 DEBUG2("rlm_dbm: Reply found");
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,
230 DEBUG("rlm_dbm: recived: %d\n",res);
232 case RLM_MODULE_NOTFOUND:
235 default: /* seems error code */
236 parse_state = SMP_ERROR;
237 DEBUG2("rlm_dbm: Nested record error\n");
240 join_attr = join_attr -> next;
242 pairdelete(&vp,SM_JOIN_ATTR, 0, TAG_ANY);
243 if ( parse_state != SMP_ERROR ) {
244 if ( ! isfallthrough(vp) ) {
246 DEBUG2("rlm_dbm: Break search due Fall-Through = no");
248 pairmove(&vp,&nu_reply);
250 pairmove(&tmp_reply,&vp);
252 parse_state = SMP_PATTERN;
253 found = RLM_MODULE_OK;
256 pairfree(&nu_reply); }
258 default: /* we do not wait that !!!! */
259 parse_state = SMP_ERROR;
260 DEBUG2("rlm_dbm: Unknown token: %d\n",retcod);
265 if ( parse_state == SMP_PATTERN ) {
266 pairmove(config,&tmp_config);
267 pairfree(&tmp_config);
268 pairmove(reply,&tmp_reply);
269 pairfree(&tmp_reply);
271 pairfree(&tmp_config);
272 pairfree(&tmp_reply);
274 DEBUG2("rlm_dbm: Bad final parse state: %d\n",parse_state);
275 found = RLM_MODULE_FAIL ;
281 static int sm_postprocessor(VALUE_PAIR **reply UNUSED) {
285 static int rlm_dbm_instantiate(CONF_SECTION *conf, void **instance)
287 struct rlm_dbm_t *inst;
289 *instance = inst = talloc_zero(conf, struct rlm_dbm_t);
290 if (!inst) return -1;
292 if (cf_section_parse(conf, inst, module_config) < 0) {
298 static rlm_rcode_t rlm_dbm_authorize(void *instance, REQUEST *request)
300 VALUE_PAIR *namepair;
301 VALUE_PAIR *request_pairs;
302 VALUE_PAIR *check_tmp = NULL;
303 VALUE_PAIR *reply_tmp = NULL;
307 SM_USER_ENTRY *ulist = NULL;
310 struct rlm_dbm_t *inst = instance;
312 VALUE_PAIR **check_pairs, **reply_pairs;
314 request_pairs = request->packet->vps;
315 check_pairs = &request->config_items;
316 reply_pairs = &request->reply->vps;
319 * Grab the canonical user name.
321 namepair = request->username;
322 name = namepair ? (char *) namepair->vp_strvalue : "NONE";
324 DEBUG2("rlm_dbm: try open database file: %s\n",inst -> userfile);
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);
336 found = RLM_MODULE_FAIL;
337 DEBUG2("rlm_dbm: Cannot open database file: %s\n",
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;
348 sm_user_list_wipe(&ulist);
349 pairfree(&reply_tmp);
350 pairfree(&check_tmp);
355 /* globally exported name */
359 0, /* type: reserved */
360 rlm_dbm_instantiate, /* instantiation */
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 */