Massively cleaned up #include's, so they're in a consistent
[freeradius.git] / src / modules / rlm_detail / rlm_detail.c
1 /*
2  * rlm_detail.c accounting:    Write the "detail" files.
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 2000,2006  The FreeRADIUS server project
21  */
22
23 #include        <freeradius-devel/ident.h>
24 RCSID("$Id$")
25
26 #include        <freeradius-devel/radiusd.h>
27 #include        <freeradius-devel/modules.h>
28 #include        <freeradius-devel/rad_assert.h>
29
30 #include        <sys/stat.h>
31 #include        <ctype.h>
32 #include        <fcntl.h>
33
34 #define         DIRLEN  8192
35
36 static const char *packet_codes[] = {
37   "",
38   "Access-Request",
39   "Access-Accept",
40   "Access-Reject",
41   "Accounting-Request",
42   "Accounting-Response",
43   "Accounting-Status",
44   "Password-Request",
45   "Password-Accept",
46   "Password-Reject",
47   "Accounting-Message",
48   "Access-Challenge"
49 };
50
51
52 struct detail_instance {
53         /* detail file */
54         char *detailfile;
55
56         /* detail file permissions */
57         int detailperm;
58
59         /* directory permissions */
60         int dirperm;
61
62         /* last made directory */
63         char *last_made_directory;
64
65         /* timestamp & stuff */
66         char *header;
67
68         /* if we want file locking */
69         int locking;
70
71         /* log src/dst information */
72         int log_srcdst;
73
74         lrad_hash_table_t *ht;
75 };
76
77 static const CONF_PARSER module_config[] = {
78         { "detailfile",    PW_TYPE_STRING_PTR,
79           offsetof(struct detail_instance,detailfile), NULL, "%A/%{Client-IP-Address}/detail" },
80         { "header",    PW_TYPE_STRING_PTR,
81           offsetof(struct detail_instance,header), NULL, "%t" },
82         { "detailperm",    PW_TYPE_INTEGER,
83           offsetof(struct detail_instance,detailperm), NULL, "0600" },
84         { "dirperm",       PW_TYPE_INTEGER,
85           offsetof(struct detail_instance,dirperm),    NULL, "0755" },
86         { "locking",       PW_TYPE_BOOLEAN,
87           offsetof(struct detail_instance,locking),    NULL, "no" },
88         { "log_packet_header",       PW_TYPE_BOOLEAN,
89           offsetof(struct detail_instance,log_srcdst),    NULL, "no" },
90         { NULL, -1, 0, NULL, NULL }
91 };
92
93
94 /*
95  *      Clean up.
96  */
97 static int detail_detach(void *instance)
98 {
99         struct detail_instance *inst = instance;
100         free((char*) inst->last_made_directory);
101         if (inst->ht) lrad_hash_table_free(inst->ht);
102
103         free(inst);
104         return 0;
105 }
106
107
108 static uint32_t detail_hash(const void *data)
109 {
110         const DICT_ATTR *da = data;
111         return lrad_hash(&(da->attr), sizeof(da->attr));
112 }
113
114 static int detail_cmp(const void *a, const void *b)
115 {
116         return ((const DICT_ATTR *)a)->attr - ((const DICT_ATTR *)b)->attr;
117 }
118
119
120 /*
121  *      (Re-)read radiusd.conf into memory.
122  */
123 static int detail_instantiate(CONF_SECTION *conf, void **instance)
124 {
125         struct detail_instance *inst;
126         CONF_SECTION    *cs;
127
128         inst = rad_malloc(sizeof(*inst));
129         if (!inst) {
130                 return -1;
131         }
132         memset(inst, 0, sizeof(*inst));
133
134         if (cf_section_parse(conf, inst, module_config) < 0) {
135                 detail_detach(inst);
136                 return -1;
137         }
138
139         inst->last_made_directory = NULL;
140
141         /*
142          *      Suppress certain attributes.
143          */
144         cs = cf_section_sub_find(conf, "suppress");
145         if (cs) {
146                 CONF_ITEM       *ci;
147
148                 inst->ht = lrad_hash_table_create(detail_hash, detail_cmp,
149                                                   NULL);
150
151                 for (ci = cf_item_find_next(cs, NULL);
152                      ci != NULL;
153                      ci = cf_item_find_next(cs, ci)) {
154                         const char      *attr;
155                         DICT_ATTR       *da;
156
157                         if (!cf_item_is_pair(ci)) continue;
158                                                 
159                         attr = cf_pair_attr(cf_itemtopair(ci));
160                         if (!attr) continue; /* pair-anoia */
161
162                         da = dict_attrbyname(attr);
163                         if (!da) {
164                                 radlog(L_INFO, "rlm_detail: WARNING: No such attribute %s: Cannot suppress printing it.", attr);
165                                 continue;
166                         }
167
168                         /*
169                          *      For better distribution we should really
170                          *      hash the attribute number or name.  But
171                          *      since the suppression list will usually
172                          *      be small, it doesn't matter.
173                          */
174                         if (!lrad_hash_table_insert(inst->ht, da)) {
175                                 radlog(L_ERR, "rlm_detail: Failed trying to remember %s", attr);
176                                 detail_detach(inst);
177                                 return -1;
178                         }
179                 }
180         }
181
182
183         *instance = inst;
184         return 0;
185 }
186
187 /*
188  *      Do detail, compatible with old accounting
189  */
190 static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
191                      int compat)
192 {
193         int             outfd;
194         FILE            *outfp;
195         char            timestamp[256];
196         char            buffer[DIRLEN];
197         char            *p;
198         struct stat     st;
199         int             locked;
200         int             lock_count;
201         struct timeval  tv;
202         VALUE_PAIR      *pair;
203
204         struct detail_instance *inst = instance;
205
206         /*
207          *      Nothing to log: don't do anything.
208          */
209         if (!packet) {
210                 return RLM_MODULE_NOOP;
211         }
212
213         /*
214          *      Create a directory for this nas.
215          *
216          *      Generate the path for the detail file.  Use the
217          *      same format, but truncate at the last /.  Then
218          *      feed it through radius_xlat() to expand the
219          *      variables.
220          */
221         radius_xlat(buffer, sizeof(buffer), inst->detailfile, request, NULL);
222         DEBUG2("rlm_detail: %s expands to %s", inst->detailfile, buffer);
223
224         /*
225          *      Grab the last directory delimiter.
226          */
227         p = strrchr(buffer,'/');
228
229         /*
230          *      There WAS a directory delimiter there, and
231          *      the file doesn't exist, so
232          *      we prolly must create it the dir(s)
233          */
234         if ((p) && (stat(buffer, &st) < 0)) {
235                 *p = '\0';
236                 /*
237                  *      NO previously cached directory name, so we've
238                  *      got to create a new one.
239                  *
240                  *      OR the new directory name is different than the old,
241                  *      so we've got to create a new one.
242                  *
243                  *      OR the cached directory has somehow gotten removed,
244                  *      so we've got to create a new one.
245                  */
246                 if ((inst->last_made_directory == NULL) ||
247                     (strcmp(inst->last_made_directory, buffer) != 0)) {
248                         free((char *) inst->last_made_directory);
249                         inst->last_made_directory = strdup(buffer);
250                 }
251
252                 /*
253                  *      stat the directory, and don't do anything if
254                  *      it exists.  If it doesn't exist, create it.
255                  *
256                  *      This also catches the case where some idiot
257                  *      deleted a directory that the server was using.
258                  */
259                 if (rad_mkdir(inst->last_made_directory, inst->dirperm) < 0) {
260                         radlog(L_ERR, "rlm_detail: Failed to create directory %s: %s", inst->last_made_directory, strerror(errno));
261                         return RLM_MODULE_FAIL;
262                 }
263
264                 *p = '/';
265         } /* else there was no directory delimiter. */
266
267         locked = 0;
268         lock_count = 0;
269         do {
270                 /*
271                  *      Open & create the file, with the given
272                  *      permissions.
273                  */
274                 if ((outfd = open(buffer, O_WRONLY | O_APPEND | O_CREAT,
275                                   inst->detailperm)) < 0) {
276                         radlog(L_ERR, "rlm_detail: Couldn't open file %s: %s",
277                                buffer, strerror(errno));
278                         return RLM_MODULE_FAIL;
279                 }
280
281                 /*
282                  *      If we fail to aquire the filelock in 80 tries
283                  *      (approximately two seconds) we bail out.
284                  */
285                 if (inst->locking) {
286                         lseek(outfd, 0L, SEEK_SET);
287                         if (rad_lockfd_nonblock(outfd, 0) < 0) {
288                                 close(outfd);
289                                 tv.tv_sec = 0;
290                                 tv.tv_usec = 25000;
291                                 select(0, NULL, NULL, NULL, &tv);
292                                 lock_count++;
293                                 continue;
294                         }
295
296                         /*
297                          *      The file might have been deleted by
298                          *      radrelay while we tried to acquire
299                          *      the lock (race condition)
300                          */
301                         if (fstat(outfd, &st) != 0) {
302                                 radlog(L_ERR, "rlm_detail: Couldn't stat file %s: %s",
303                                        buffer, strerror(errno));
304                                 close(outfd);
305                                 return RLM_MODULE_FAIL;
306                         }
307                         if (st.st_nlink == 0) {
308                                 DEBUG("rlm_detail: File %s removed by another program, retrying",
309                                       buffer);
310                                 close(outfd);
311                                 lock_count = 0;
312                                 continue;
313                         }
314
315                         DEBUG("rlm_detail: Acquired filelock, tried %d time(s)",
316                               lock_count + 1);
317                         locked = 1;
318                 }
319         } while (inst->locking && !locked && lock_count < 80);
320
321         if (inst->locking && !locked) {
322                 close(outfd);
323                 radlog(L_ERR, "rlm_detail: Failed to aquire filelock for %s, giving up",
324                        buffer);
325                 return RLM_MODULE_FAIL;
326         }
327
328         /*
329          *      Convert the FD to FP.  The FD is no longer valid
330          *      after this operation.
331          */
332         if ((outfp = fdopen(outfd, "a")) == NULL) {
333                 radlog(L_ERR, "rlm_detail: Couldn't open file %s: %s",
334                        buffer, strerror(errno));
335                 if (inst->locking) {
336                         lseek(outfd, 0L, SEEK_SET);
337                         rad_unlockfd(outfd, 0);
338                         DEBUG("rlm_detail: Released filelock");
339                 }
340                 close(outfd);   /* automatically releases the lock */
341
342                 return RLM_MODULE_FAIL;
343         }
344
345         /*
346          *      Post a timestamp
347          */
348         fseek(outfp, 0L, SEEK_END);
349         radius_xlat(timestamp, sizeof(timestamp), inst->header, request, NULL);
350         fprintf(outfp, "%s\n", timestamp);
351
352         /*
353          *      Write the information to the file.
354          */
355         if (!compat) {
356                 /*
357                  *      Print out names, if they're OK.
358                  *      Numbers, if not.
359                  */
360                 if ((packet->code > 0) &&
361                     (packet->code <= PW_ACCESS_CHALLENGE)) {
362                         fprintf(outfp, "\tPacket-Type = %s\n",
363                                 packet_codes[packet->code]);
364                 } else {
365                         fprintf(outfp, "\tPacket-Type = %d\n", packet->code);
366                 }
367         }
368
369         if (inst->log_srcdst) {
370                 VALUE_PAIR src_vp, dst_vp;
371
372                 src_vp.name[0] = dst_vp.name[0] = '\0'; /* for vp_prints() */
373                 src_vp.operator = dst_vp.operator = T_OP_EQ;
374
375                 switch (packet->src_ipaddr.af) {
376                 case AF_INET:
377                         src_vp.type = PW_TYPE_IPADDR;
378                         src_vp.attribute = PW_PACKET_SRC_IP_ADDRESS;
379                         src_vp.lvalue = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
380                         dst_vp.type = PW_TYPE_IPADDR;
381                         dst_vp.attribute = PW_PACKET_DST_IP_ADDRESS;
382                         dst_vp.lvalue = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
383                         break;
384                 case AF_INET6:
385                         src_vp.type = PW_TYPE_IPV6ADDR; 
386                         src_vp.attribute = PW_PACKET_SRC_IPV6_ADDRESS;
387                         memcpy(src_vp.vp_strvalue,
388                                &packet->src_ipaddr.ipaddr.ip6addr,
389                                sizeof(packet->src_ipaddr.ipaddr.ip6addr));
390                         dst_vp.type = PW_TYPE_IPV6ADDR; 
391                         dst_vp.attribute = PW_PACKET_DST_IPV6_ADDRESS;
392                         memcpy(dst_vp.vp_strvalue,
393                                &packet->dst_ipaddr.ipaddr.ip6addr,
394                                sizeof(packet->dst_ipaddr.ipaddr.ip6addr));
395                         break;
396                 default:
397                         break;
398                 }
399
400                 fputs("\t", outfp);
401                 vp_print(outfp, &src_vp);
402                 fputs("\n", outfp);
403                 fputs("\t", outfp);
404                 vp_print(outfp, &dst_vp);
405                 fputs("\n", outfp);
406
407                 src_vp.attribute = PW_PACKET_SRC_PORT;
408                 src_vp.type = PW_TYPE_INTEGER;
409                 src_vp.lvalue = packet->src_port;
410                 dst_vp.attribute = PW_PACKET_DST_PORT;
411                 dst_vp.type = PW_TYPE_INTEGER;
412                 dst_vp.lvalue = packet->dst_port;
413
414                 fputs("\t", outfp);
415                 vp_print(outfp, &src_vp);
416                 fputs("\n", outfp);
417                 fputs("\t", outfp);
418                 vp_print(outfp, &dst_vp);
419                 fputs("\n", outfp);
420         }
421
422         /* Write each attribute/value to the log file */
423         for (pair = packet->vps; pair != NULL; pair = pair->next) {
424                 DICT_ATTR da;
425                 da.attr = pair->attribute;
426
427                 if (inst->ht &&
428                     lrad_hash_table_finddata(inst->ht, &da)) continue;
429
430                 /*
431                  *      Don't print passwords in old format...
432                  */
433                 if (compat && (pair->attribute == PW_USER_PASSWORD)) continue;
434
435                 /*
436                  *      Print all of the attributes.
437                  */
438                 fputs("\t", outfp);
439                 vp_print(outfp, pair);
440                 fputs("\n", outfp);
441         }
442
443         /*
444          *      Add non-protocol attibutes.
445          */
446         if (compat) {
447                 if (request->proxy) {
448                         char proxy_buffer[128];
449
450                         inet_ntop(request->proxy->dst_ipaddr.af,
451                                   &request->proxy->dst_ipaddr.ipaddr,
452                                   proxy_buffer, sizeof(proxy_buffer));
453                         fprintf(outfp, "\tFreeradius-Proxied-To = %s\n",
454                                         proxy_buffer);
455                                 DEBUG("rlm_detail: Freeradius-Proxied-To = %s",
456                                       proxy_buffer);
457                 }
458
459                 fprintf(outfp, "\tTimestamp = %ld\n",
460                         (unsigned long) request->timestamp);
461
462                 if (request->packet->verified == 2)
463                         fputs("\tRequest-Authenticator = Verified\n", outfp);
464                 else if (request->packet->verified == 1)
465                         fputs("\tRequest-Authenticator = None\n", outfp);
466         }
467
468         fputs("\n", outfp);
469
470         if (inst->locking) {
471                 fflush(outfp);
472                 lseek(outfd, 0L, SEEK_SET);
473                 rad_unlockfd(outfd, 0);
474                 DEBUG("rlm_detail: Released filelock");
475         }
476
477         fclose(outfp);
478
479         /*
480          *      And everything is fine.
481          */
482         return RLM_MODULE_OK;
483 }
484
485 /*
486  *      Accounting - write the detail files.
487  */
488 static int detail_accounting(void *instance, REQUEST *request)
489 {
490
491         return do_detail(instance,request,request->packet, TRUE);
492 }
493
494 /*
495  *      Incoming Access Request - write the detail files.
496  */
497 static int detail_authorize(void *instance, REQUEST *request)
498 {
499         return do_detail(instance,request,request->packet, FALSE);
500 }
501
502 /*
503  *      Outgoing Access-Request Reply - write the detail files.
504  */
505 static int detail_postauth(void *instance, REQUEST *request)
506 {
507         return do_detail(instance,request,request->reply, FALSE);
508 }
509
510
511 /*
512  *      Outgoing Access-Request to home server - write the detail files.
513  */
514 static int detail_pre_proxy(void *instance, REQUEST *request)
515 {
516         if (request->proxy &&
517             request->proxy->vps) {
518                 return do_detail(instance,request,request->proxy, FALSE);
519         }
520
521         return RLM_MODULE_NOOP;
522 }
523
524
525 /*
526  *      Outgoing Access-Request Reply - write the detail files.
527  */
528 static int detail_post_proxy(void *instance, REQUEST *request)
529 {
530         if (request->proxy_reply &&
531             request->proxy_reply->vps) {
532                 return do_detail(instance,request,request->proxy_reply, FALSE);
533         }
534
535         return RLM_MODULE_NOOP;
536 }
537
538
539 /* globally exported name */
540 module_t rlm_detail = {
541         RLM_MODULE_INIT,
542         "detail",
543         RLM_TYPE_THREAD_UNSAFE,        /* type: reserved */
544         detail_instantiate,             /* instantiation */
545         detail_detach,                  /* detach */
546         {
547                 NULL,                   /* authentication */
548                 detail_authorize,       /* authorization */
549                 NULL,                   /* preaccounting */
550                 detail_accounting,      /* accounting */
551                 NULL,                   /* checksimul */
552                 detail_pre_proxy,       /* pre-proxy */
553                 detail_post_proxy,      /* post-proxy */
554                 detail_postauth         /* post-auth */
555         },
556 };
557