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.
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.
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2004,2006 The FreeRADIUS server project
21 * Copyright 2004 Alan DeKok <aland@freeradius.org>
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
28 #include <freeradius-devel/rad_assert.h>
29 #include <freeradius-devel/exfile.h>
52 * Define a structure for our module configuration.
54 typedef struct rlm_linelog_t {
58 bool escape; //!< do filename escaping, yes / no
60 xlat_escape_t escape_func; //!< escape function
62 char const *syslog_facility; //!< Syslog facility string.
63 char const *syslog_severity; //!< Syslog severity string.
64 int syslog_priority; //!< Bitwise | of severity and facility.
69 char const *reference;
74 * A mapping of configuration file names to internal variables.
76 * Note that the string is dynamically allocated, so it MUST
77 * be freed. When the configuration file parse re-reads the string,
78 * it free's the old one, and strdup's the new one, placing the pointer
79 * to the strdup'd string into 'config.string'. This gets around
82 static const CONF_PARSER module_config[] = {
83 { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_OUTPUT | PW_TYPE_REQUIRED | PW_TYPE_XLAT, rlm_linelog_t, filename), NULL },
84 { "escape_filenames", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_linelog_t, escape), "no" },
85 { "syslog_facility", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_linelog_t, syslog_facility), NULL },
86 { "syslog_severity", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_linelog_t, syslog_severity), "info" },
87 { "permissions", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_linelog_t, permissions), "0600" },
88 { "group", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_linelog_t, group), NULL },
89 { "format", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_linelog_t, line), NULL },
90 { "reference", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_linelog_t, reference), NULL },
91 CONF_PARSER_TERMINATOR
96 * Instantiate the module.
98 static int mod_instantiate(CONF_SECTION *conf, void *instance)
100 rlm_linelog_t *inst = instance;
103 if (!inst->filename) {
104 cf_log_err_cs(conf, "No value provided for 'filename'");
109 * Escape filenames only if asked.
112 inst->escape_func = rad_filename_escape;
114 inst->escape_func = rad_filename_make_safe;
117 #ifndef HAVE_SYSLOG_H
118 if (strcmp(inst->filename, "syslog") == 0) {
119 cf_log_err_cs(conf, "Syslog output is not supported on this system");
124 if (inst->syslog_facility) {
125 num = fr_str2int(syslog_facility_table, inst->syslog_facility, -1);
127 cf_log_err_cs(conf, "Invalid syslog facility \"%s\"", inst->syslog_facility);
131 inst->syslog_priority |= num;
134 num = fr_str2int(syslog_severity_table, inst->syslog_severity, -1);
136 cf_log_err_cs(conf, "Invalid syslog severity \"%s\"", inst->syslog_severity);
139 inst->syslog_priority |= num;
142 if (!inst->line && !inst->reference) {
143 cf_log_err_cs(conf, "Must specify a log format, or reference");
147 inst->ef = exfile_init(inst, 64, 30, true);
149 cf_log_err_cs(conf, "Failed creating log file context");
159 * Escape unprintable characters.
161 static size_t linelog_escape_func(UNUSED REQUEST *request,
162 char *out, size_t outlen, char const *in,
167 if (outlen == 0) return 0;
176 if (outlen <= 2) break;
183 if (outlen == 1) break;
191 if (outlen <= 2) break;
199 if (outlen <= 2) break;
207 if (outlen <= 4) break;
208 snprintf(out, outlen, "\\%03o", (uint8_t) *in);
221 static rlm_rcode_t CC_HINT(nonnull) mod_do_linelog(void *instance, REQUEST *request)
226 rlm_linelog_t *inst = (rlm_linelog_t*) instance;
227 char const *value = inst->line;
236 if (inst->reference) {
242 if (radius_xlat(p, sizeof(line) - 2, request, inst->reference, linelog_escape_func, NULL) < 0) {
243 return RLM_MODULE_FAIL;
246 line[0] = '.'; /* force to be in current section */
249 * Don't allow it to go back up
251 if (line[1] == '.') goto do_log;
253 ci = cf_reference_item(NULL, inst->cs, line);
255 RDEBUG2("No such entry \"%s\"", line);
256 return RLM_MODULE_NOOP;
259 if (!cf_item_is_pair(ci)) {
260 RDEBUG2("Entry \"%s\" is not a variable assignment ", line);
264 cp = cf_item_to_pair(ci);
265 value = cf_pair_value(cp);
267 RDEBUG2("Entry \"%s\" has no value", line);
272 * Value exists, but is empty. Don't log anything.
274 if (!*value) return RLM_MODULE_OK;
279 * FIXME: Check length.
281 if (strcmp(inst->filename, "syslog") != 0) {
284 if (radius_xlat(path, sizeof(path), request, inst->filename, inst->escape_func, NULL) < 0) {
285 return RLM_MODULE_FAIL;
288 /* check path and eventually create subdirs */
289 p = strrchr(path, '/');
292 if (rad_mkdir(path, 0700, -1, -1) < 0) {
293 RERROR("rlm_linelog: Failed to create directory %s: %s", path, fr_syserror(errno));
294 return RLM_MODULE_FAIL;
299 fd = exfile_open(inst->ef, path, inst->permissions, true);
301 ERROR("rlm_linelog: Failed to open %s: %s", path, fr_syserror(errno));
302 return RLM_MODULE_FAIL;
305 if (inst->group != NULL) {
306 gid = strtol(inst->group, &endptr, 10);
307 if (*endptr != '\0') {
308 if (rad_getgid(request, &gid, inst->group) < 0) {
309 RDEBUG2("Unable to find system group \"%s\"", inst->group);
314 if (chown(path, -1, gid) == -1) {
315 RDEBUG2("Unable to change system group of \"%s\"", path);
323 * FIXME: Check length.
325 if (value && (radius_xlat(line, sizeof(line) - 1, request, value, linelog_escape_func, NULL) < 0)) {
326 if (fd >= 0) exfile_close(inst->ef, fd);
328 return RLM_MODULE_FAIL;
334 if (write(fd, line, strlen(line)) < 0) {
335 ERROR("rlm_linelog: Failed writing: %s", fr_syserror(errno));
336 exfile_close(inst->ef, fd);
337 return RLM_MODULE_FAIL;
340 exfile_close(inst->ef, fd);
344 syslog(inst->syslog_priority, "%s", line);
348 return RLM_MODULE_OK;
353 * Externally visible module definition.
355 extern module_t rlm_linelog;
356 module_t rlm_linelog = {
357 .magic = RLM_MODULE_INIT,
359 .type = RLM_TYPE_HUP_SAFE,
360 .inst_size = sizeof(rlm_linelog_t),
361 .config = module_config,
362 .instantiate = mod_instantiate,
364 [MOD_AUTHENTICATE] = mod_do_linelog,
365 [MOD_AUTHORIZE] = mod_do_linelog,
366 [MOD_PREACCT] = mod_do_linelog,
367 [MOD_ACCOUNTING] = mod_do_linelog,
368 [MOD_PRE_PROXY] = mod_do_linelog,
369 [MOD_POST_PROXY] = mod_do_linelog,
370 [MOD_POST_AUTH] = mod_do_linelog,
372 [MOD_RECV_COA] = mod_do_linelog,
373 [MOD_SEND_COA] = mod_do_linelog