66fedf3857460f7bbddc226cb1b0a23847769a9c
[freeradius.git] / src / modules / rlm_linelog / rlm_linelog.c
1 /*
2  * rlm_linelog.c
3  *
4  * Version:     $Id$
5  *
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.
10  *
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.
15  *
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
19  *
20  * Copyright 2004,2006  The FreeRADIUS server project
21  * Copyright 2004  Alan DeKok <aland@freeradius.org>
22  */
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_FCNTL_H
31 #include <fcntl.h>
32 #endif
33
34 /*
35  *      Define a structure for our module configuration.
36  *
37  *      These variables do not need to be in a structure, but it's
38  *      a lot cleaner to do so, and a pointer to the structure can
39  *      be used as the instance handle.
40  */
41 typedef struct rlm_linelog_t {
42         char            *filename;
43         char            *line;
44 } rlm_linelog_t;
45
46 /*
47  *      A mapping of configuration file names to internal variables.
48  *
49  *      Note that the string is dynamically allocated, so it MUST
50  *      be freed.  When the configuration file parse re-reads the string,
51  *      it free's the old one, and strdup's the new one, placing the pointer
52  *      to the strdup'd string into 'config.string'.  This gets around
53  *      buffer over-flows.
54  */
55 static const CONF_PARSER module_config[] = {
56         { "filename",  PW_TYPE_STRING_PTR,
57           offsetof(rlm_linelog_t,filename), NULL,  NULL},
58         { "format",  PW_TYPE_STRING_PTR,
59           offsetof(rlm_linelog_t,line), NULL,  NULL},
60         { NULL, -1, 0, NULL, NULL }             /* end the list */
61 };
62
63
64 static int linelog_detach(void *instance)
65 {
66         rlm_linelog_t *inst = instance;
67
68         
69         free(inst);
70         return 0;
71 }
72
73 /*
74  *      Instantiate the module.
75  */
76 static int linelog_instantiate(CONF_SECTION *conf, void **instance)
77 {
78         rlm_linelog_t *inst;
79
80         /*
81          *      Set up a storage area for instance data
82          */
83         inst = rad_malloc(sizeof(*inst));
84         memset(inst, 0, sizeof(*inst));
85
86         /*
87          *      If the configuration parameters can't be parsed, then
88          *      fail.
89          */
90         if (cf_section_parse(conf, inst, module_config) < 0) {
91                 linelog_detach(inst);
92                 return -1;
93         }
94
95         *instance = inst;
96
97         return 0;
98 }
99
100
101 /*
102  *      Escape unprintable characters.
103  */
104 static int linelog_escape_func(char *out, int outlen, const char *in)
105 {
106         int len = 0;
107
108         if (outlen == 0) return 0;
109         if (outlen == 1) {
110                 *out = '\0';
111                 return 0;
112         }
113
114         while (in[0]) {
115                 if (in[0] >= ' ') {
116                         if (in[0] == '\\') {
117                                 if (outlen <= 2) break;
118                                 outlen--;
119                                 *out++ = '\\';
120                                 len++;
121                         }
122
123                         outlen--;
124                         if (outlen == 1) break;
125                         *out++ = *in++;
126                         len++;
127                         continue;
128                 }
129
130                 switch (in[0]) {
131                 case '\n':
132                         if (outlen <= 2) break;
133                         *out++ = '\\';
134                         *out++ = 'n';
135                         in++;
136                         len += 2;
137                         break;
138
139                 case '\r':
140                         if (outlen <= 2) break;
141                         *out++ = '\\';
142                         *out++ = 'r';
143                         in++;
144                         len += 2;
145                         break;
146
147                 default:
148                         if (outlen <= 4) break;
149                         snprintf(out, outlen,  "\\%03o", *in);
150                         in++;
151                         out += 4;
152                         outlen -= 4;
153                         len += 4;
154                         break;
155                 }
156         }
157
158         *out = '\0';
159         return len;
160 }
161
162 static int do_linelog(void *instance, REQUEST *request)
163 {
164         int fd;
165         char buffer[4096];
166         char line[1024];
167         rlm_linelog_t *inst;
168
169         inst = (rlm_linelog_t*) instance;
170
171         /*
172          *      FIXME: Check length.
173          */
174         radius_xlat(buffer, sizeof(buffer), inst->filename, request, NULL);
175
176         fd = open(buffer, O_WRONLY | O_APPEND | O_CREAT, 0600);
177         if (fd == -1) {
178                 radlog(L_ERR, "rlm_linelog: Failed to open %s: %s",
179                        buffer, strerror(errno));
180                 return RLM_MODULE_FAIL;
181         }
182
183         /*
184          *      FIXME: Check length.
185          */
186         radius_xlat(line, sizeof(line) - 1, inst->line, request,
187                     linelog_escape_func);
188         strcat(line, "\n");
189         
190         write(fd, line, strlen(line));
191         close(fd);
192
193         return RLM_MODULE_OK;
194 }
195
196
197 /*
198  *      Externally visible module definition.
199  */
200 module_t rlm_linelog = {
201         RLM_MODULE_INIT,
202         "linelog",
203         RLM_TYPE_THREAD_SAFE,           /* type */
204         linelog_instantiate,            /* instantiation */
205         linelog_detach,                 /* detach */
206         {
207                 do_linelog,     /* authentication */
208                 do_linelog,     /* authorization */
209                 do_linelog,     /* preaccounting */
210                 do_linelog,     /* accounting */
211                 NULL,           /* checksimul */
212                 do_linelog,     /* pre-proxy */
213                 do_linelog,     /* post-proxy */
214                 do_linelog      /* post-auth */
215         },
216 };