~ubuntu-branches/ubuntu/hardy/freeradius/hardy-proposed

« back to all changes in this revision

Viewing changes to src/modules/rlm_sql_log/rlm_sql_log.c

  • Committer: Bazaar Package Importer
  • Author(s): Mark Hymers
  • Date: 2006-12-16 20:45:11 UTC
  • mfrom: (3.1.10 feisty)
  • Revision ID: james.westby@ubuntu.com-20061216204511-3pbbsu4s8jtehsor
Tags: 1.1.3-3
Fix POSIX compliance problem in init script.  Closes: #403384. 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *  rlm_sql_log.c       Append the SQL queries in a log file which
 
3
 *                      is read later by the radsqlrelay program
 
4
 *
 
5
 *  Version:    $Id: rlm_sql_log.c,v 1.3.2.2 2005/12/12 17:44:35 nbk Exp $
 
6
 *
 
7
 *  Author:     Nicolas Baradakis <nicolas.baradakis@cegetel.net>
 
8
 *
 
9
 *  Copyright (C) 2005 Cegetel
 
10
 *
 
11
 *  This program is free software; you can redistribute it and/or
 
12
 *  modify it under the terms of the GNU General Public License
 
13
 *  as published by the Free Software Foundation; either version 2
 
14
 *  of the License, or (at your option) any later version.
 
15
 *
 
16
 *  This program is distributed in the hope that it will be useful,
 
17
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
18
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 
19
 *  GNU General Public License for more details.
 
20
 *
 
21
 *  You should have received a copy of the GNU General Public License
 
22
 *  along with this program; if not, write to the Free Software
 
23
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
24
 */
 
25
 
 
26
#include "autoconf.h"
 
27
 
 
28
#include <fcntl.h>
 
29
#include <stdio.h>
 
30
#include <stdlib.h>
 
31
#include <sys/stat.h>
 
32
 
 
33
#include "libradius.h"
 
34
#include "radiusd.h"
 
35
#include "modules.h"
 
36
#include "conffile.h"
 
37
 
 
38
static const char rcsid[] = "$Id: rlm_sql_log.c,v 1.3.2.2 2005/12/12 17:44:35 nbk Exp $";
 
39
 
 
40
static int sql_log_instantiate(CONF_SECTION *conf, void **instance);
 
41
static int sql_log_detach(void *instance);
 
42
static int sql_log_accounting(void *instance, REQUEST *request);
 
43
static int sql_log_postauth(void *instance, REQUEST *request);
 
44
 
 
45
#define MAX_QUERY_LEN 4096
 
46
 
 
47
/*
 
48
 *      Define a structure for our module configuration.
 
49
 */
 
50
typedef struct rlm_sql_log_t {
 
51
        char            *name;
 
52
        char            *path;
 
53
        char            *postauth_query;
 
54
        char            *sql_user_name;
 
55
        char            *allowed_chars;
 
56
        CONF_SECTION    *conf_section;
 
57
} rlm_sql_log_t;
 
58
 
 
59
/*
 
60
 *      A mapping of configuration file names to internal variables.
 
61
 */
 
62
static const CONF_PARSER module_config[] = {
 
63
        {"path", PW_TYPE_STRING_PTR,
 
64
         offsetof(rlm_sql_log_t,path), NULL, "${radacctdir}/sql-relay"},
 
65
        {"Post-Auth", PW_TYPE_STRING_PTR,
 
66
         offsetof(rlm_sql_log_t,postauth_query), NULL, ""},
 
67
        {"sql_user_name", PW_TYPE_STRING_PTR,
 
68
         offsetof(rlm_sql_log_t,sql_user_name), NULL, ""},
 
69
        {"safe-characters", PW_TYPE_STRING_PTR,
 
70
         offsetof(rlm_sql_log_t,allowed_chars), NULL,
 
71
        "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
 
72
 
 
73
        { NULL, -1, 0, NULL, NULL }     /* end the list */
 
74
};
 
75
 
 
76
static char *allowed_chars = NULL;
 
77
 
 
78
/*
 
79
 *      Do any per-module initialization that is separate to each
 
80
 *      configured instance of the module.  e.g. set up connections
 
81
 *      to external databases, read configuration files, set up
 
82
 *      dictionary entries, etc.
 
83
 *
 
84
 *      If configuration information is given in the config section
 
85
 *      that must be referenced in later calls, store a handle to it
 
86
 *      in *instance otherwise put a null pointer there.
 
87
 */
 
88
static int sql_log_instantiate(CONF_SECTION *conf, void **instance)
 
89
{
 
90
        const char      *name;
 
91
        rlm_sql_log_t   *inst;
 
92
 
 
93
        /*
 
94
         *      Set up a storage area for instance data.
 
95
         */
 
96
        inst = calloc(1, sizeof(rlm_sql_log_t));
 
97
        if (inst == NULL) {
 
98
                radlog(L_ERR, "rlm_sql_log: Not enough memory");
 
99
                return -1;
 
100
        }
 
101
 
 
102
        /*
 
103
         *      Get the name of the current section in the conf file.
 
104
         */
 
105
        name = cf_section_name2(conf);
 
106
        if (name == NULL)
 
107
                name = cf_section_name1(conf);
 
108
        if (name == NULL)
 
109
                name = "sql_log";
 
110
        inst->name = strdup(name);
 
111
 
 
112
        /*
 
113
         *      If the configuration parameters can't be parsed,
 
114
         *      then fail.
 
115
         */
 
116
        if (cf_section_parse(conf, inst, module_config) < 0) {
 
117
                radlog(L_ERR, "rlm_sql_log (%s): Unable to parse parameters",
 
118
                       inst->name);
 
119
                sql_log_detach(inst);
 
120
                return -1;
 
121
        }
 
122
 
 
123
        inst->conf_section = conf;
 
124
        allowed_chars = inst->allowed_chars;
 
125
        *instance = inst;
 
126
        return 0;
 
127
}
 
128
 
 
129
/*
 
130
 *      Say goodbye to the cruel world.
 
131
 */
 
132
static int sql_log_detach(void *instance)
 
133
{
 
134
        int i;
 
135
        char **p;
 
136
        rlm_sql_log_t *inst = (rlm_sql_log_t *)instance;
 
137
 
 
138
        if (inst->name) {
 
139
                free(inst->name);
 
140
                inst->name = NULL;
 
141
        }
 
142
 
 
143
        /*
 
144
         *      Free up dynamically allocated string pointers.
 
145
         */
 
146
        for (i = 0; module_config[i].name != NULL; i++) {
 
147
                if (module_config[i].type != PW_TYPE_STRING_PTR) {
 
148
                        continue;
 
149
                }
 
150
 
 
151
                /*
 
152
                 *      Treat 'config' as an opaque array of bytes,
 
153
                 *      and take the offset into it.  There's a
 
154
                 *      (char*) pointer at that offset, and we want
 
155
                 *      to point to it.
 
156
                 */
 
157
                p = (char **) (((char *)inst) + module_config[i].offset);
 
158
                if (!*p) { /* nothing allocated */
 
159
                        continue;
 
160
                }
 
161
                free(*p);
 
162
                *p = NULL;
 
163
        }
 
164
        allowed_chars = NULL;
 
165
        free(inst);
 
166
        return 0;
 
167
}
 
168
 
 
169
/*
 
170
 *      Translate the SQL queries.
 
171
 */
 
172
static int sql_escape_func(char *out, int outlen, const char *in)
 
173
{
 
174
        int len = 0;
 
175
 
 
176
        while (in[0]) {
 
177
                /*
 
178
                 *      Non-printable characters get replaced with their
 
179
                 *      mime-encoded equivalents.
 
180
                 */
 
181
                if ((in[0] < 32) ||
 
182
                    strchr(allowed_chars, *in) == NULL) {
 
183
                        /*
 
184
                         *      Only 3 or less bytes available.
 
185
                         */
 
186
                        if (outlen <= 3) {
 
187
                                break;
 
188
                        }
 
189
 
 
190
                        snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
 
191
                        in++;
 
192
                        out += 3;
 
193
                        outlen -= 3;
 
194
                        len += 3;
 
195
                        continue;
 
196
                }
 
197
 
 
198
                /*
 
199
                 *      Only one byte left.
 
200
                 */
 
201
                if (outlen <= 1) {
 
202
                        break;
 
203
                }
 
204
 
 
205
                /*
 
206
                 *      Allowed character.
 
207
                 */
 
208
                *out = *in;
 
209
                out++;
 
210
                in++;
 
211
                outlen--;
 
212
                len++;
 
213
        }
 
214
        *out = '\0';
 
215
        return len;
 
216
}
 
217
 
 
218
/*
 
219
 *      Add the 'SQL-User-Name' attribute to the packet.
 
220
 */
 
221
static int sql_set_user(rlm_sql_log_t *inst, REQUEST *request, char *sqlusername, const char *username)
 
222
{
 
223
        VALUE_PAIR *vp=NULL;
 
224
        char tmpuser[MAX_STRING_LEN];
 
225
 
 
226
        tmpuser[0] = '\0';
 
227
        sqlusername[0] = '\0';
 
228
 
 
229
        /* Remove any user attr we added previously */
 
230
        pairdelete(&request->packet->vps, PW_SQL_USER_NAME);
 
231
 
 
232
        if (username != NULL) {
 
233
                strNcpy(tmpuser, username, MAX_STRING_LEN);
 
234
        } else if (inst->sql_user_name[0] != '\0') {
 
235
                radius_xlat(tmpuser, sizeof(tmpuser), inst->sql_user_name,
 
236
                            request, NULL);
 
237
        } else {
 
238
                return 0;
 
239
        }
 
240
 
 
241
        if (tmpuser[0] != '\0') {
 
242
                strNcpy(sqlusername, tmpuser, sizeof(tmpuser));
 
243
                DEBUG2("rlm_sql_log (%s): sql_set_user escaped user --> '%s'",
 
244
                       inst->name, sqlusername);
 
245
                vp = pairmake("SQL-User-Name", sqlusername, 0);
 
246
                if (vp == NULL) {
 
247
                        radlog(L_ERR, "%s", librad_errstr);
 
248
                        return -1;
 
249
                }
 
250
 
 
251
                pairadd(&request->packet->vps, vp);
 
252
                return 0;
 
253
        }
 
254
        return -1;
 
255
}
 
256
 
 
257
/*
 
258
 *      Replace %<whatever> in the query.
 
259
 */
 
260
static int sql_xlat_query(rlm_sql_log_t *inst, REQUEST *request, const char *query, char *xlat_query, size_t len)
 
261
{
 
262
        char    sqlusername[MAX_STRING_LEN];
 
263
 
 
264
        /* If query is not defined, we stop here */
 
265
        if (query[0] == '\0')
 
266
                return RLM_MODULE_NOOP;
 
267
 
 
268
        /* Add attribute 'SQL-User-Name' */
 
269
        if (sql_set_user(inst, request, sqlusername, NULL) <0) {
 
270
                radlog(L_ERR, "rlm_sql_log (%s): Couldn't add SQL-User-Name attribute",
 
271
                               inst->name);
 
272
                return RLM_MODULE_FAIL;
 
273
        }
 
274
 
 
275
        /* Expand variables in the query */
 
276
        xlat_query[0] = '\0';
 
277
        radius_xlat(xlat_query, len, query, request, sql_escape_func);
 
278
        if (xlat_query[0] == '\0') {
 
279
                radlog(L_ERR, "rlm_sql_log (%s): Couldn't xlat the query %s",
 
280
                       inst->name, query);
 
281
                return RLM_MODULE_FAIL;
 
282
        }
 
283
 
 
284
        return RLM_MODULE_OK;
 
285
}
 
286
 
 
287
/*
 
288
 *      The Perl version of radsqlrelay uses fcntl locks.
 
289
 */
 
290
static int setlock(int fd)
 
291
{
 
292
        struct flock fl;
 
293
        memset(&fl, 0, sizeof(fl));
 
294
        fl.l_start = 0;
 
295
        fl.l_len = 0;
 
296
        fl.l_type = F_WRLCK;
 
297
        fl.l_whence = SEEK_SET;
 
298
        return fcntl(fd, F_SETLKW, &fl);
 
299
}
 
300
 
 
301
/*
 
302
 *      Write the line into file (with lock)
 
303
 */
 
304
static int sql_log_write(rlm_sql_log_t *inst, REQUEST *request, const char *line)
 
305
{
 
306
        int fd;
 
307
        FILE *fp;
 
308
        int locked = 0;
 
309
        struct stat st;
 
310
        char path[MAX_STRING_LEN];
 
311
 
 
312
        path[0] = '\0';
 
313
        radius_xlat(path, sizeof(path), inst->path, request, NULL);
 
314
        if (path[0] == '\0')
 
315
                return RLM_MODULE_FAIL;
 
316
 
 
317
        while (!locked) {
 
318
                if ((fd = open(path, O_WRONLY | O_APPEND | O_CREAT, 0666)) < 0) {
 
319
                        radlog(L_ERR, "rlm_sql_log (%s): Couldn't open file %s: %s",
 
320
                               inst->name, path, strerror(errno));
 
321
                        return RLM_MODULE_FAIL;
 
322
                }
 
323
                if (setlock(fd) != 0) {
 
324
                        radlog(L_ERR, "rlm_sql_log (%s): Couldn't lock file %s: %s",
 
325
                               inst->name, path, strerror(errno));
 
326
                        close(fd);
 
327
                        return RLM_MODULE_FAIL;
 
328
                }
 
329
                if (fstat(fd, &st) != 0) {
 
330
                        radlog(L_ERR, "rlm_sql_log (%s): Couldn't stat file %s: %s",
 
331
                               inst->name, path, strerror(errno));
 
332
                        close(fd);
 
333
                        return RLM_MODULE_FAIL;
 
334
                }
 
335
                if (st.st_nlink == 0) {
 
336
                        DEBUG("rlm_sql_log (%s): File %s removed by another program, retrying",
 
337
                              inst->name, path);
 
338
                        close(fd);
 
339
                        continue;
 
340
                }
 
341
                locked = 1;
 
342
        }
 
343
 
 
344
        if ((fp = fdopen(fd, "a")) == NULL) {
 
345
                radlog(L_ERR, "rlm_sql_log (%s): Couldn't associate a stream with file %s: %s",
 
346
                       inst->name, path, strerror(errno));
 
347
                close(fd);
 
348
                return RLM_MODULE_FAIL;
 
349
        }
 
350
        fputs(line, fp);
 
351
        putc('\n', fp);
 
352
        fclose(fp);     /* and unlock */
 
353
        return RLM_MODULE_OK;
 
354
}
 
355
 
 
356
/*
 
357
 *      Write accounting information to this module's database.
 
358
 */
 
359
static int sql_log_accounting(void *instance, REQUEST *request)
 
360
{
 
361
        int             ret;
 
362
        char            querystr[MAX_QUERY_LEN];
 
363
        char            *cfquery;
 
364
        rlm_sql_log_t   *inst = (rlm_sql_log_t *)instance;
 
365
        VALUE_PAIR      *pair;
 
366
        DICT_VALUE      *dval;
 
367
        CONF_PAIR       *cp;
 
368
 
 
369
        DEBUG("rlm_sql_log (%s): Processing sql_log_accounting", inst->name);
 
370
 
 
371
        /* Find the Acct Status Type. */
 
372
        if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) == NULL) {
 
373
                radlog(L_ERR, "rlm_sql_log (%s): Packet has no account status type",
 
374
                       inst->name);
 
375
                return RLM_MODULE_INVALID;
 
376
        }
 
377
 
 
378
        /* Search the query in conf section of the module */
 
379
        if ((dval = dict_valbyattr(PW_ACCT_STATUS_TYPE, pair->lvalue)) == NULL) {
 
380
                radlog(L_ERR, "rlm_sql_log (%s): Unsupported Acct-Status-Type = %d",
 
381
                       inst->name, pair->lvalue);
 
382
                return RLM_MODULE_NOOP;
 
383
        }
 
384
        if ((cp = cf_pair_find(inst->conf_section, dval->name)) == NULL) {
 
385
                DEBUG("rlm_sql_log (%s): Couldn't find an entry %s in the config section",
 
386
                      inst->name, dval->name);
 
387
                return RLM_MODULE_NOOP;
 
388
        }
 
389
        cfquery = cf_pair_value(cp);
 
390
 
 
391
        /* Xlat the query */
 
392
        ret = sql_xlat_query(inst, request, cfquery, querystr, sizeof(querystr));
 
393
        if (ret != RLM_MODULE_OK)
 
394
                return ret;
 
395
 
 
396
        /* Write query into sql-relay file */
 
397
        return sql_log_write(inst, request, querystr);
 
398
}
 
399
 
 
400
/*
 
401
 *      Write post-auth information to this module's database.
 
402
 */
 
403
static int sql_log_postauth(void *instance, REQUEST *request)
 
404
{
 
405
        int             ret;
 
406
        char            querystr[MAX_QUERY_LEN];
 
407
        rlm_sql_log_t   *inst = (rlm_sql_log_t *)instance;
 
408
 
 
409
        DEBUG("rlm_sql_log (%s): Processing sql_log_postauth", inst->name);
 
410
 
 
411
        /* Xlat the query */
 
412
        ret = sql_xlat_query(inst, request, inst->postauth_query,
 
413
                             querystr, sizeof(querystr));
 
414
        if (ret != RLM_MODULE_OK)
 
415
                return ret;
 
416
 
 
417
        /* Write query into sql-relay file */
 
418
        return sql_log_write(inst, request, querystr);
 
419
}
 
420
 
 
421
/*
 
422
 *      The module name should be the only globally exported symbol.
 
423
 *      That is, everything else should be 'static'.
 
424
 *
 
425
 *      If the module needs to temporarily modify it's instantiation
 
426
 *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
 
427
 *      The server will then take care of ensuring that the module
 
428
 *      is single-threaded.
 
429
 */
 
430
module_t rlm_sql_log = {
 
431
        "sql_log",
 
432
        RLM_TYPE_THREAD_SAFE,           /* type */
 
433
        NULL,                           /* initialization */
 
434
        sql_log_instantiate,            /* instantiation */
 
435
        {
 
436
                NULL,                   /* authentication */
 
437
                NULL,                   /* authorization */
 
438
                NULL,                   /* preaccounting */
 
439
                sql_log_accounting,     /* accounting */
 
440
                NULL,                   /* checksimul */
 
441
                NULL,                   /* pre-proxy */
 
442
                NULL,                   /* post-proxy */
 
443
                sql_log_postauth        /* post-auth */
 
444
        },
 
445
        sql_log_detach,                 /* detach */
 
446
        NULL,                           /* destroy */
 
447
};