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;
41 * A temporary holding area for config values to be extracted
42 * into, before they are copied into the instance data
44 static struct detail_instance config;
46 static CONF_PARSER module_config[] = {
47 { "detailfile", PW_TYPE_STRING_PTR, &config.detailfile, "%A/%n/detail" },
48 { "detailperm", PW_TYPE_INTEGER, &config.detailperm, "0600" },
49 { "dirperm", PW_TYPE_INTEGER, &config.dirperm, "0755" },
50 { NULL, -1, NULL, NULL }
54 * (Re-)read radiusd.conf into memory.
56 static int detail_instantiate(CONF_SECTION *conf, void **instance)
58 struct detail_instance *inst;
61 inst = malloc(sizeof *inst);
63 radlog(L_ERR|L_CONS, "rlm_detail: Out of memory\n");
67 if (cf_section_parse(conf, module_config) < 0) {
73 * Sanitize the name for security! Only permit letters, numbers,
74 * -, _, / and \. Anything else will be rejected.
77 p = strspn(config.detailfile, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_/%.");
79 if (p != strlen(config.detailfile)) {
80 radlog(L_ERR|L_CONS, "rlm_detail: Illegal character in detail filename.");
84 inst->detailfile = config.detailfile;
85 inst->detailperm = config.detailperm;
86 inst->dirperm = config.dirperm;
87 inst->last_made_directory = NULL;
88 config.detailfile = NULL;
95 * Accounting - write the detail files.
97 static int detail_accounting(void *instance, REQUEST *request)
105 int ret = RLM_MODULE_OK;
108 struct detail_instance *inst = instance;
111 * Create a directory for this nas.
113 * Generate the path for the detail file. Use the
114 * same format, but truncate at the last /. Then
115 * feed it through radius_xlat2() to expand the
118 radius_xlat2(buffer, sizeof(buffer), inst->detailfile, request,
119 request->reply->vps);
122 * Sanitize the name for security! Only permit letters, numbers,
123 * -, _, / and \. Anything else will be rejected.
126 if (strstr(buffer, "..")) {
127 radlog(L_ERR, "rlm_detail: Directory \"%s\" contains \"..\" which is not valid.",
129 return RLM_MODULE_FAIL;
132 l = strspn(buffer, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_/.");
134 if (l != strlen(buffer)) {
135 radlog(L_ERR, "rlm_detail: Directory \"%s\" contains an invalid character.",
137 return RLM_MODULE_FAIL;
141 * Grab the last directory delimiter.
143 p = strrchr(buffer,'/');
146 * There WAS a directory delimiter there, so let's
147 * go create the relevant directories.
153 * NO previously cached directory name, so we've
154 * got to create a new one.
156 * OR the new directory name is different than the old,
157 * so we've got to create a new one.
159 * OR the cached directory has somehow gotten removed,
160 * so we've got to create a new one.
162 if ((inst->last_made_directory == NULL) ||
163 (strcmp(inst->last_made_directory, buffer) != 0) ||
164 (stat(buffer, &st) == -1)) {
167 * Free any previously cached name.
169 if (inst->last_made_directory != NULL) {
170 free((char *) inst->last_made_directory);
171 inst->last_made_directory = NULL;
175 * Try to create the new directory.
177 if ((mkdir(buffer, inst->dirperm) == -1) &&
179 radlog(L_ERR, "rlm_detail: Couldn't create %s: %s",
180 buffer, strerror(errno));
182 return RLM_MODULE_FAIL;
186 * Save a copy of the directory name that
189 inst->last_made_directory = strdup(buffer);
193 * Re-create the file name, by replacing the
194 * directory delimiter that we removed above.
197 } /* else there was no directory delimiter. */
200 * Open & create the file, with the given permissions.
202 if ((outfd = open(buffer, O_WRONLY|O_APPEND|O_CREAT,
203 inst->detailperm)) < 0) {
204 radlog(L_ERR, "rlm_detail: Couldn't open file %s: %s",
205 buffer, strerror(errno));
206 ret = RLM_MODULE_FAIL;
208 } else if ((outfp = fdopen(outfd, "a")) == NULL) {
209 radlog(L_ERR, "rlm_detail: Couldn't open file %s: %s",
210 buffer, strerror(errno));
211 ret = RLM_MODULE_FAIL;
214 /* Post a timestamp */
215 fputs(ctime(&request->timestamp), outfp);
217 /* Write each attribute/value to the log file */
218 pair = request->packet->vps;
220 if (pair->attribute != PW_PASSWORD) {
222 vp_print(outfp, pair);
229 * Add non-protocol attibutes.
231 fprintf(outfp, "\tTimestamp = %ld\n", request->timestamp);
232 if (request->packet->verified)
233 fputs("\tRequest-Authenticator = Verified\n", outfp);
235 fputs("\tRequest-Authenticator = None\n", outfp);
247 static int detail_detach(void *instance)
249 struct detail_instance *inst = instance;
250 free((char *) inst->detailfile);
252 if (inst->last_made_directory)
253 free((char*) inst->last_made_directory);
259 /* globally exported name */
260 module_t rlm_detail = {
262 0, /* type: reserved */
263 NULL, /* initialization */
264 detail_instantiate, /* instantiation */
265 NULL, /* authorization */
266 NULL, /* authentication */
267 NULL, /* preaccounting */
268 detail_accounting, /* accounting */
269 NULL, /* checksimul */
270 detail_detach, /* detach */