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