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