Enable building WITHOUT_ACCOUNTING
[freeradius.git] / src / modules / rlm_detail / rlm_detail.c
index 03677d3..8436317 100644 (file)
  *
  *   You should have received a copy of the GNU General Public License
  *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  *
- * Copyright 2000  The FreeRADIUS server project
+ * Copyright 2000,2006  The FreeRADIUS server project
  */
 
-static const char rcsid[] = "$Id$";
+#include       <freeradius-devel/ident.h>
+RCSID("$Id$")
 
-#include       "autoconf.h"
+#include       <freeradius-devel/radiusd.h>
+#include       <freeradius-devel/modules.h>
+#include       <freeradius-devel/rad_assert.h>
+#include       <freeradius-devel/detail.h>
 
 #include       <sys/stat.h>
-#include       <sys/select.h>
-
-#include       <stdlib.h>
-#include       <string.h>
 #include       <ctype.h>
 #include       <fcntl.h>
 
-#include       "radiusd.h"
-#include       "modules.h"
-#include       "rad_assert.h"
-
 #define        DIRLEN  8192
 
-static const char *packet_codes[] = {
-  "",
-  "Access-Request",
-  "Access-Accept",
-  "Access-Reject",
-  "Accounting-Request",
-  "Accounting-Response",
-  "Accounting-Status",
-  "Password-Request",
-  "Password-Accept",
-  "Password-Reject",
-  "Accounting-Message",
-  "Access-Challenge"
-};
-
-
 struct detail_instance {
        /* detail file */
        char *detailfile;
@@ -72,6 +52,11 @@ struct detail_instance {
 
        /* if we want file locking */
        int locking;
+
+       /* log src/dst information */
+       int log_srcdst;
+
+       fr_hash_table_t *ht;
 };
 
 static const CONF_PARSER module_config[] = {
@@ -85,15 +70,45 @@ static const CONF_PARSER module_config[] = {
          offsetof(struct detail_instance,dirperm),    NULL, "0755" },
        { "locking",       PW_TYPE_BOOLEAN,
          offsetof(struct detail_instance,locking),    NULL, "no" },
+       { "log_packet_header",       PW_TYPE_BOOLEAN,
+         offsetof(struct detail_instance,log_srcdst),    NULL, "no" },
        { NULL, -1, 0, NULL, NULL }
 };
 
+
+/*
+ *     Clean up.
+ */
+static int detail_detach(void *instance)
+{
+        struct detail_instance *inst = instance;
+       free((char*) inst->last_made_directory);
+       if (inst->ht) fr_hash_table_free(inst->ht);
+
+        free(inst);
+       return 0;
+}
+
+
+static uint32_t detail_hash(const void *data)
+{
+       const DICT_ATTR *da = data;
+       return fr_hash(&(da->attr), sizeof(da->attr));
+}
+
+static int detail_cmp(const void *a, const void *b)
+{
+       return ((const DICT_ATTR *)a)->attr - ((const DICT_ATTR *)b)->attr;
+}
+
+
 /*
  *     (Re-)read radiusd.conf into memory.
  */
 static int detail_instantiate(CONF_SECTION *conf, void **instance)
 {
        struct detail_instance *inst;
+       CONF_SECTION    *cs;
 
        inst = rad_malloc(sizeof(*inst));
        if (!inst) {
@@ -102,12 +117,54 @@ static int detail_instantiate(CONF_SECTION *conf, void **instance)
        memset(inst, 0, sizeof(*inst));
 
        if (cf_section_parse(conf, inst, module_config) < 0) {
-               free(inst);
+               detail_detach(inst);
                return -1;
        }
 
        inst->last_made_directory = NULL;
 
+       /*
+        *      Suppress certain attributes.
+        */
+       cs = cf_section_sub_find(conf, "suppress");
+       if (cs) {
+               CONF_ITEM       *ci;
+
+               inst->ht = fr_hash_table_create(detail_hash, detail_cmp,
+                                                 NULL);
+
+               for (ci = cf_item_find_next(cs, NULL);
+                    ci != NULL;
+                    ci = cf_item_find_next(cs, ci)) {
+                       const char      *attr;
+                       DICT_ATTR       *da;
+
+                       if (!cf_item_is_pair(ci)) continue;
+
+                       attr = cf_pair_attr(cf_itemtopair(ci));
+                       if (!attr) continue; /* pair-anoia */
+
+                       da = dict_attrbyname(attr);
+                       if (!da) {
+                               radlog(L_INFO, "rlm_detail: WARNING: No such attribute %s: Cannot suppress printing it.", attr);
+                               continue;
+                       }
+
+                       /*
+                        *      For better distribution we should really
+                        *      hash the attribute number or name.  But
+                        *      since the suppression list will usually
+                        *      be small, it doesn't matter.
+                        */
+                       if (!fr_hash_table_insert(inst->ht, da)) {
+                               radlog(L_ERR, "rlm_detail: Failed trying to remember %s", attr);
+                               detail_detach(inst);
+                               return -1;
+                       }
+               }
+       }
+
+
        *instance = inst;
        return 0;
 }
@@ -127,12 +184,12 @@ static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
        int             locked;
        int             lock_count;
        struct timeval  tv;
-       REALM           *proxy_realm;
-       char            proxy_buffer[16];
-       VALUE_PAIR      *pair = packet->vps;
+       VALUE_PAIR      *pair;
 
        struct detail_instance *inst = instance;
 
+       rad_assert(request != NULL);
+
        /*
         *      Nothing to log: don't do anything.
         */
@@ -149,7 +206,7 @@ static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
         *      variables.
         */
        radius_xlat(buffer, sizeof(buffer), inst->detailfile, request, NULL);
-       DEBUG2("rlm_detail: %s expands to %s", inst->detailfile, buffer);
+       RDEBUG2("%s expands to %s", inst->detailfile, buffer);
 
        /*
         *      Grab the last directory delimiter.
@@ -175,15 +232,7 @@ static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
                 */
                if ((inst->last_made_directory == NULL) ||
                    (strcmp(inst->last_made_directory, buffer) != 0)) {
-
-                       /*
-                        *      Free any previously cached name.
-                        */
-                       if (inst->last_made_directory != NULL) {
-                               free((char *) inst->last_made_directory);
-                               inst->last_made_directory = NULL;
-                       }
-
+                       free((char *) inst->last_made_directory);
                        inst->last_made_directory = strdup(buffer);
                }
 
@@ -195,7 +244,7 @@ static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
                 *      deleted a directory that the server was using.
                 */
                if (rad_mkdir(inst->last_made_directory, inst->dirperm) < 0) {
-                       radlog(L_ERR, "rlm_detail: Failed to create directory %s: %s", inst->last_made_directory, strerror(errno));
+                       radlog_request(L_ERR, 0, request, "rlm_detail: Failed to create directory %s: %s", inst->last_made_directory, strerror(errno));
                        return RLM_MODULE_FAIL;
                }
 
@@ -211,7 +260,7 @@ static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
                 */
                if ((outfd = open(buffer, O_WRONLY | O_APPEND | O_CREAT,
                                  inst->detailperm)) < 0) {
-                       radlog(L_ERR, "rlm_detail: Couldn't open file %s: %s",
+                       radlog_request(L_ERR, 0, request, "rlm_detail: Couldn't open file %s: %s",
                               buffer, strerror(errno));
                        return RLM_MODULE_FAIL;
                }
@@ -237,20 +286,20 @@ static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
                         *      the lock (race condition)
                         */
                        if (fstat(outfd, &st) != 0) {
-                               radlog(L_ERR, "rlm_detail: Couldn't stat file %s: %s",
+                               radlog_request(L_ERR, 0, request, "rlm_detail: Couldn't stat file %s: %s",
                                       buffer, strerror(errno));
                                close(outfd);
                                return RLM_MODULE_FAIL;
                        }
                        if (st.st_nlink == 0) {
-                               DEBUG("rlm_detail: File %s removed by another program, retrying",
+                               RDEBUG2("File %s removed by another program, retrying",
                                      buffer);
                                close(outfd);
                                lock_count = 0;
                                continue;
                        }
 
-                       DEBUG("rlm_detail: Acquired filelock, tried %d time(s)",
+                       RDEBUG2("Acquired filelock, tried %d time(s)",
                              lock_count + 1);
                        locked = 1;
                }
@@ -258,7 +307,7 @@ static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
 
        if (inst->locking && !locked) {
                close(outfd);
-               radlog(L_ERR, "rlm_detail: Failed to aquire filelock for %s, giving up",
+               radlog_request(L_ERR, 0, request, "rlm_detail: Failed to acquire filelock for %s, giving up",
                       buffer);
                return RLM_MODULE_FAIL;
        }
@@ -268,12 +317,12 @@ static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
         *      after this operation.
         */
        if ((outfp = fdopen(outfd, "a")) == NULL) {
-               radlog(L_ERR, "rlm_detail: Couldn't open file %s: %s",
+               radlog_request(L_ERR, 0, request, "rlm_detail: Couldn't open file %s: %s",
                       buffer, strerror(errno));
                if (inst->locking) {
                        lseek(outfd, 0L, SEEK_SET);
                        rad_unlockfd(outfd, 0);
-                       DEBUG("rlm_detail: Released filelock");
+                       RDEBUG2("Released filelock");
                }
                close(outfd);   /* automatically releases the lock */
 
@@ -281,6 +330,13 @@ static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
        }
 
        /*
+        *      Post a timestamp
+        */
+       fseek(outfp, 0L, SEEK_END);
+       radius_xlat(timestamp, sizeof(timestamp), inst->header, request, NULL);
+       fprintf(outfp, "%s\n", timestamp);
+
+       /*
         *      Write the information to the file.
         */
        if (!compat) {
@@ -289,30 +345,80 @@ static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
                 *      Numbers, if not.
                 */
                if ((packet->code > 0) &&
-                   (packet->code <= PW_ACCESS_CHALLENGE)) {
-                       fprintf(outfp, "Packet-Type = %s\n",
-                               packet_codes[packet->code]);
+                   (packet->code < FR_MAX_PACKET_CODE)) {
+                       fprintf(outfp, "\tPacket-Type = %s\n",
+                               fr_packet_codes[packet->code]);
                } else {
-                       fprintf(outfp, "Packet-Type = %d\n", packet->code);
+                       fprintf(outfp, "\tPacket-Type = %d\n", packet->code);
                }
        }
 
-       /*
-        *      Post a timestamp
-        */
-       fseek(outfp, 0L, SEEK_END);
-       radius_xlat(timestamp, sizeof(timestamp), inst->header, request, NULL);
-       fprintf(outfp, "%s\n", timestamp);
+       if (inst->log_srcdst) {
+               VALUE_PAIR src_vp, dst_vp;
+
+               memset(&src_vp, 0, sizeof(src_vp));
+               memset(&dst_vp, 0, sizeof(dst_vp));
+               src_vp.operator = dst_vp.operator = T_OP_EQ;
+
+               switch (packet->src_ipaddr.af) {
+               case AF_INET:
+                       src_vp.type = PW_TYPE_IPADDR;
+                       src_vp.attribute = PW_PACKET_SRC_IP_ADDRESS;
+                       src_vp.vp_ipaddr = packet->src_ipaddr.ipaddr.ip4addr.s_addr;
+                       dst_vp.type = PW_TYPE_IPADDR;
+                       dst_vp.attribute = PW_PACKET_DST_IP_ADDRESS;
+                       dst_vp.vp_ipaddr = packet->dst_ipaddr.ipaddr.ip4addr.s_addr;
+                       break;
+               case AF_INET6:
+                       src_vp.type = PW_TYPE_IPV6ADDR;
+                       src_vp.attribute = PW_PACKET_SRC_IPV6_ADDRESS;
+                       memcpy(src_vp.vp_strvalue,
+                              &packet->src_ipaddr.ipaddr.ip6addr,
+                              sizeof(packet->src_ipaddr.ipaddr.ip6addr));
+                       dst_vp.type = PW_TYPE_IPV6ADDR;
+                       dst_vp.attribute = PW_PACKET_DST_IPV6_ADDRESS;
+                       memcpy(dst_vp.vp_strvalue,
+                              &packet->dst_ipaddr.ipaddr.ip6addr,
+                              sizeof(packet->dst_ipaddr.ipaddr.ip6addr));
+                       break;
+               default:
+                       break;
+               }
+
+               fputs("\t", outfp);
+               vp_print(outfp, &src_vp);
+               fputs("\n", outfp);
+               fputs("\t", outfp);
+               vp_print(outfp, &dst_vp);
+               fputs("\n", outfp);
+
+               src_vp.attribute = PW_PACKET_SRC_PORT;
+               src_vp.type = PW_TYPE_INTEGER;
+               src_vp.vp_integer = packet->src_port;
+               dst_vp.attribute = PW_PACKET_DST_PORT;
+               dst_vp.type = PW_TYPE_INTEGER;
+               dst_vp.vp_integer = packet->dst_port;
+
+               fputs("\t", outfp);
+               vp_print(outfp, &src_vp);
+               fputs("\n", outfp);
+               fputs("\t", outfp);
+               vp_print(outfp, &dst_vp);
+               fputs("\n", outfp);
+       }
 
        /* Write each attribute/value to the log file */
-       while (pair) {
+       for (pair = packet->vps; pair != NULL; pair = pair->next) {
+               DICT_ATTR da;
+               da.attr = pair->attribute;
+
+               if (inst->ht &&
+                   fr_hash_table_finddata(inst->ht, &da)) continue;
+
                /*
                 *      Don't print passwords in old format...
                 */
-               if (compat && (pair->attribute == PW_PASSWORD)) {
-                       pair = pair->next;
-                       continue;
-               }
+               if (compat && (pair->attribute == PW_USER_PASSWORD)) continue;
 
                /*
                 *      Print all of the attributes.
@@ -320,37 +426,34 @@ static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
                fputs("\t", outfp);
                vp_print(outfp, pair);
                fputs("\n", outfp);
-               pair = pair->next;
        }
 
        /*
         *      Add non-protocol attibutes.
         */
        if (compat) {
-               if ((pair = pairfind(request->config_items,
-                                    PW_PROXY_TO_REALM)) != NULL) {
-                       proxy_realm = realm_find(pair->strvalue, TRUE);
-                       if (proxy_realm) {
-                               memset((char *) proxy_buffer, 0, 16);
-
-                               rad_assert(proxy_realm->acct_ipaddr.af == AF_INET);
-
-                               inet_ntop(proxy_realm->acct_ipaddr.af,
-                                         &proxy_realm->acct_ipaddr.ipaddr,
-                                         proxy_buffer, sizeof(proxy_buffer));
-                               fprintf(outfp, "\tFreeradius-Proxied-To = %s\n",
+#ifdef WITH_PROXY
+               if (request->proxy) {
+                       char proxy_buffer[128];
+
+                       inet_ntop(request->proxy->dst_ipaddr.af,
+                                 &request->proxy->dst_ipaddr.ipaddr,
+                                 proxy_buffer, sizeof(proxy_buffer));
+                       fprintf(outfp, "\tFreeradius-Proxied-To = %s\n",
                                        proxy_buffer);
-                               DEBUG("rlm_detail: Freeradius-Proxied-To set to %s",
+                               RDEBUG("Freeradius-Proxied-To = %s",
                                      proxy_buffer);
-                       }
                }
+#endif
+
                fprintf(outfp, "\tTimestamp = %ld\n",
                        (unsigned long) request->timestamp);
 
-               if (request->packet->verified == 2)
-                       fputs("\tRequest-Authenticator = Verified\n", outfp);
-               else if (request->packet->verified == 1)
-                       fputs("\tRequest-Authenticator = None\n", outfp);
+               /*
+                *      We no longer permit Accounting-Request packets
+                *      with an authenticator of zero.
+                */
+               fputs("\tRequest-Authenticator = Verified\n", outfp);
        }
 
        fputs("\n", outfp);
@@ -359,7 +462,7 @@ static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
                fflush(outfp);
                lseek(outfd, 0L, SEEK_SET);
                rad_unlockfd(outfd, 0);
-               DEBUG("rlm_detail: Released filelock");
+               RDEBUG2("Released filelock");
        }
 
        fclose(outfp);
@@ -375,6 +478,14 @@ static int do_detail(void *instance, REQUEST *request, RADIUS_PACKET *packet,
  */
 static int detail_accounting(void *instance, REQUEST *request)
 {
+#ifdef WITH_DETAIL
+       if (request->listener->type == RAD_LISTEN_DETAIL &&
+           strcmp(((struct detail_instance *)instance)->detailfile,
+                  ((listen_detail_t *)request->listener->data)->filename) == 0) {
+               RDEBUG("Suppressing writes to detail file as the request was just read from a detail file.");
+               return RLM_MODULE_NOOP;
+       }
+#endif
 
        return do_detail(instance,request,request->packet, TRUE);
 }
@@ -395,10 +506,28 @@ static int detail_postauth(void *instance, REQUEST *request)
        return do_detail(instance,request,request->reply, FALSE);
 }
 
+#ifdef WITH_COA
+/*
+ *     Incoming CoA - write the detail files.
+ */
+static int detail_recv_coa(void *instance, REQUEST *request)
+{
+       return do_detail(instance,request,request->packet, FALSE);
+}
+
+/*
+ *     Outgoing CoA - write the detail files.
+ */
+static int detail_send_coa(void *instance, REQUEST *request)
+{
+       return do_detail(instance,request,request->reply, FALSE);
+}
+#endif
 
 /*
  *     Outgoing Access-Request to home server - write the detail files.
  */
+#ifdef WITH_PROXY
 static int detail_pre_proxy(void *instance, REQUEST *request)
 {
        if (request->proxy &&
@@ -420,42 +549,52 @@ static int detail_post_proxy(void *instance, REQUEST *request)
                return do_detail(instance,request,request->proxy_reply, FALSE);
        }
 
-       return RLM_MODULE_NOOP;
-}
-
+       /*
+        *      No reply: we must be doing Post-Proxy-Type = Fail.
+        *
+        *      Note that we just call the normal accounting function,
+        *      to minimize the amount of code, and to highlight that
+        *      it's doing normal accounting.
+        */
+       if (!request->proxy_reply) {
+               int rcode;
 
-/*
- *     Clean up.
- */
-static int detail_detach(void *instance)
-{
-        struct detail_instance *inst = instance;
-       free((char *) inst->detailfile);
+               rcode = detail_accounting(instance, request);
+               if (rcode == RLM_MODULE_OK) {
+                       request->reply->code = PW_ACCOUNTING_RESPONSE;
+               }
+               return rcode;
+       }
 
-       if (inst->last_made_directory)
-               free((char*) inst->last_made_directory);
-        free(inst);
-       return 0;
+       return RLM_MODULE_NOOP;
 }
+#endif
 
 
 /* globally exported name */
 module_t rlm_detail = {
+       RLM_MODULE_INIT,
        "detail",
-       RLM_TYPE_THREAD_UNSAFE,        /* type: reserved */
-       NULL,                           /* initialization */
+       RLM_TYPE_THREAD_UNSAFE | RLM_TYPE_CHECK_CONFIG_SAFE | RLM_TYPE_HUP_SAFE,
        detail_instantiate,             /* instantiation */
+       detail_detach,                  /* detach */
        {
                NULL,                   /* authentication */
                detail_authorize,       /* authorization */
                NULL,                   /* preaccounting */
                detail_accounting,      /* accounting */
                NULL,                   /* checksimul */
+#ifdef WITH_PROXY
                detail_pre_proxy,       /* pre-proxy */
                detail_post_proxy,      /* post-proxy */
+#else
+               NULL, NULL,
+#endif
                detail_postauth         /* post-auth */
+#ifdef WITH_COA
+               , detail_recv_coa,
+               detail_send_coa
+#endif
        },
-       detail_detach,                  /* detach */
-       NULL                            /* destroy */
 };