2 * rlm_detail.c accounting: Write the "detail" files.
8 static const char rcsid[] = "$Id$";
26 struct detail_instance {
28 const char *detailfile;
30 /* detail file permissions */
33 /* directory permissions */
36 /* last made directory */
37 const char *last_made_directory;
40 static int detail_init(void)
46 * A temporary holding area for config values to be extracted
47 * into, before they are copied into the instance data
49 static struct detail_instance config;
51 static CONF_PARSER module_config[] = {
52 { "detailfile", PW_TYPE_STRING_PTR, &config.detailfile, "%A/%n/detail" },
53 { "detailperm", PW_TYPE_INTEGER, &config.detailperm, "0600" },
54 { "dirperm", PW_TYPE_INTEGER, &config.dirperm, "0755" },
55 { NULL, -1, NULL, NULL }
59 * (Re-)read radiusd.conf into memory.
61 static int detail_instantiate(CONF_SECTION *conf, void **instance)
63 struct detail_instance *inst;
66 inst = malloc(sizeof *inst);
68 radlog(L_ERR|L_CONS, "Out of memory\n");
72 if (cf_section_parse(conf, module_config) < 0) {
78 * Sanitize the name for security! Only permit letters, numbers,
79 * -, _, / and \. Anything else will be rejected.
82 p = strspn(config.detailfile, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_/%.");
84 if (p!=strlen(config.detailfile)) {
85 radlog(L_ERR|L_CONS, "Illegal character.");
89 inst->detailfile = config.detailfile;
90 inst->detailperm = config.detailperm;
91 inst->dirperm = config.dirperm;
92 inst->last_made_directory = NULL;
93 config.detailfile = NULL;
100 * Accounting - write the detail files.
102 static int detail_accounting(void *instance, REQUEST *request)
114 int ret = RLM_MODULE_OK;
116 struct detail_instance *inst = instance;
119 * Find out the name of this terminal server. We try
120 * to find the PW_NAS_IP_ADDRESS in the naslist file.
121 * If that fails, we look for the originating address.
122 * Only if that fails we resort to a name lookup.
125 nas = request->packet->src_ipaddr;
126 if ((pair = pairfind(request->packet->vps, PW_NAS_IP_ADDRESS)) != NULL)
128 if (request->proxy && request->proxy->src_ipaddr)
129 nas = request->proxy->src_ipaddr;
131 if ((cl = nas_find(nas)) != NULL) {
132 if (cl->shortname[0])
133 strcpy(nasname, cl->shortname);
135 strcpy(nasname, cl->longname);
139 ip_hostname(nasname, sizeof(nasname), nas);
143 * Create a directory for this nas.
145 * Generate the path for the detail file. Use the
146 * same format, but truncate at the last /. Then
147 * feed it through radius_xlat2() to expand the
150 strNcpy(filename, inst->detailfile, sizeof(filename));
152 radius_xlat2(buffer, sizeof(buffer), filename, request,
153 request->reply->vps);
156 * Sanitize the name for security! Only permit letters, numbers,
157 * -, _, / and \. Anything else will be rejected.
160 if(strstr(buffer, "..")) {
161 radlog(L_ERR, "Detail: Directory \"%s\" contains \"..\" which is not valid.",
163 return RLM_MODULE_FAIL;
166 l = strspn(buffer, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_/.");
168 if (l!=strlen(buffer)) {
169 radlog(L_ERR, "Detail: Directory \"%s\" contains an invalid character.",
171 return RLM_MODULE_FAIL;
174 p = strrchr(buffer,'/');
177 * There WAS a directory delimiter there, so let's
178 * go create the relevant directories.
184 * NO previously cached directory name, so we've
185 * got to create a new one.
187 * OR the new directory name is different than the old,
188 * so we've got to create a new one.
190 if ((inst->last_made_directory == NULL) ||
191 (strcmp(inst->last_made_directory, buffer) != 0)) {
194 * Free any previously cached name.
196 if (inst->last_made_directory != NULL) {
197 free((char *) inst->last_made_directory);
198 inst->last_made_directory = NULL;
202 * Try to create the new directory.
204 if ((mkdir(buffer, inst->dirperm) == -1) &&
206 radlog(L_ERR, "Detail: Couldn't create %s: %s",
207 buffer, strerror(errno));
209 return RLM_MODULE_FAIL;
213 * Save a copy of the directory name that
216 inst->last_made_directory = strdup(buffer);
220 * Re-create the file name, by replacing the
221 * directory delimiter that we removed above.
224 } /* else there was no directory delimiter. */
227 * Open & create the file, with the given permissions.
229 if ((outfd = open(buffer, O_WRONLY|O_APPEND|O_CREAT,
230 inst->detailperm)) < 0) {
231 radlog(L_ERR, "Detail: Couldn't open file %s: %s",
232 buffer, strerror(errno));
233 ret = RLM_MODULE_FAIL;
235 } else if ((outfp = fdopen(outfd, "a")) == NULL) {
236 radlog(L_ERR, "Detail: Couldn't open file %s: %s",
237 buffer, strerror(errno));
238 ret = RLM_MODULE_FAIL;
241 /* Post a timestamp */
242 fputs(ctime(&request->timestamp), outfp);
244 /* Write each attribute/value to the log file */
245 pair = request->packet->vps;
247 if (pair->attribute != PW_PASSWORD) {
249 vp_print(outfp, pair);
256 * Add non-protocol attibutes.
258 fprintf(outfp, "\tTimestamp = %ld\n", request->timestamp);
259 if (request->packet->verified)
260 fputs("\tRequest-Authenticator = Verified\n", outfp);
262 fputs("\tRequest-Authenticator = None\n", outfp);
274 static int detail_detach(void *instance)
276 struct detail_instance *inst = instance;
277 free((char *) inst->detailfile);
279 if (inst->last_made_directory)
280 free((char*) inst->last_made_directory);
286 /* globally exported name */
287 module_t rlm_detail = {
289 0, /* type: reserved */
290 detail_init, /* initialization */
291 detail_instantiate, /* instantiation */
292 NULL, /* authorization */
293 NULL, /* authentication */
294 NULL, /* preaccounting */
295 detail_accounting, /* accounting */
296 NULL, /* checksimul */
297 detail_detach, /* detach */