Finalize the radrelay additions, based on Cistron RADIUS
authoraland <aland>
Mon, 8 Jul 2002 18:23:42 +0000 (18:23 +0000)
committeraland <aland>
Mon, 8 Jul 2002 18:23:42 +0000 (18:23 +0000)
Patches from  Simon <lists@routemeister.net>

doc/rlm_detail
man/man8/radrelay.8
src/include/libradius.h
src/lib/misc.c
src/main/Makefile.in
src/main/radrelay.c
src/modules/rlm_detail/rlm_detail.c
src/modules/rlm_realm/rlm_realm.c

index a003fae..a17b517 100644 (file)
@@ -28,6 +28,7 @@
                   detailfile = %A/%{Client-IP-Address}/detail-%Y%m
                   detailperm = 0600
                   dirperm = 0755
+                  locking = no
                }
                ...
        }
   You can also use the dirperm option to specify the permissions of the
   directories created by the daemon when logging.
 
+  Setting locking to yes will enable filelocking of the detail file, this
+  is mostly useful in combination with the radrelay program included with
+  FreeRADIUS.
+
   Some of these are more useful then others, but all are available.  You
   can even have one detail file per Username.
 
index 86b4a64..82f40fd 100644 (file)
@@ -5,15 +5,21 @@ radrelay -- replicate accounting data to another RADIUS server
 .B radrelay
 .RB [ \-a
 .IR accounting_dir ]
+.RB [ \-d
+.IR radius_dir ]
 .RB [ \-f ]
 .RB [ \-i
 .IR source_ip ]
+.RB [ \-n
+.IR shortname ]
+.RB [ \-r
+.IR remote-server ]
 .RB [ \-s
 .IR secret ]
 .RB [ \-S
 .IR secret_file ]
 .RB [ \-x ]
-\fIserver detailfile\fP
+\fIdetailfile\fP
 .SH DESCRIPTION
 \fBRadrelay\fP reads a file in the \fIdetail\fP file format,
 reconstructs radius packets from it and sends them to a remote
@@ -27,12 +33,25 @@ the file, and starts over again.
 .IP "\-a \fIaccounting_directory\fP"
 The base directory to use to read the detail file from.
 
+.IP "\-d \fIradius_directory\fP"
+The base radius (raddb) directory, where the config files live.
+
 .IP \-f
 Do \fInot\fP fork and run in the background as a daemon.
 
 .IP "\-i \fIsource_ip\fP"
 The source IP address to use for sending radius packets.
 
+.IP "\-n \fIshortname\fP"
+The radius configuration files (most probably clients.conf), will be
+searched for a client section with 'shortname' set to the used argument.
+Both the server secret and the remote-server address are obtained in
+this way. Do not use the -r, -s or -S parameters in combination with -n.
+
+.IP "\-r \fIremote-server\fP"
+The remote server that will be recieving the accounting packets.
+The -r parameter can't be used in combination with -n.
+
 .IP "\-s \fIsecret\fP"
 Remote server secret.
 
@@ -41,7 +60,8 @@ Read remote server secret from file, the file should contain
 nothing other then the plain-text secret.
 
 .IP \-x
-Enable debug mode.
+Enable debug mode, -x will activate radrelay internal debugging, -xx will
+also activate librad debugging.
 
 .IP "server[:port]"
 The hostname or IP address of the remote server. Optionally a UDP port
index ff46007..3fde65d 100644 (file)
@@ -256,6 +256,7 @@ char                *strNcpy(char *dest, const char *src, int n);
 void           rad_lowercase(char *str);
 void           rad_rmspace(char *str);
 int            rad_lockfd(int fd, int lock_len);
+int            rad_lockfd_nonblock(int fd, int lock_len);
 int            rad_unlockfd(int fd, int lock_len);
 
 #ifdef ASCEND_BINARY
index 07b4c09..9d8ceca 100644 (file)
@@ -200,6 +200,21 @@ int rad_lockfd(int fd, int lock_len)
 }
 
 /*
+ *     Internal wrapper for locking, to minimize the number of ifdef's
+ *
+ *     Lock an fd, prefer lockf() over flock()
+ *     Nonblocking version.
+ */
+int rad_lockfd_nonblock(int fd, int lock_len)
+{
+#if defined(F_LOCK) && !defined(BSD)
+       return lockf(fd, F_TLOCK, lock_len);
+#else
+       return flock(fd, LOCK_EX | LOCK_NB);
+#endif
+}
+
+/*
  *     Internal wrapper for unlocking, to minimize the number of ifdef's
  *     in the source.
  *
index d5f61b1..387633c 100644 (file)
@@ -106,8 +106,8 @@ radclient: radclient.o ../lib/libradius.a
 radclient.o: radclient.c $(INCLUDES)
        $(CC) $(CFLAGS) -c radclient.c
 
-radrelay: radrelay.o ../lib/libradius.a
-       $(CC) $(CFLAGS) $(LDFLAGS) -o radrelay radrelay.o $(LIBS)
+radrelay: radrelay.o util.o nas.o client.o log.o conffile.o files.o xlat.o ../lib/libradius.a
+       $(CC) $(CFLAGS) $(LDFLAGS) -o radrelay radrelay.o util.o nas.o client.o log.o conffile.o files.o xlat.o $(LIBS)
 
 radrelay.o: radrelay.c $(INCLUDES)
        $(CC) $(CFLAGS) -c radrelay.c
index 6315bc9..5dc933c 100644 (file)
@@ -35,7 +35,9 @@ char radrelay_rcsid[] =
 #include <sys/socket.h>
 #include <sys/time.h>
 #include <sys/stat.h>
+#if HAVE_NETINET_IN_H
 #include <netinet/in.h>
+#endif
 
 #include <stdio.h>
 #include <fcntl.h>
@@ -47,12 +49,33 @@ char radrelay_rcsid[] =
 #include <errno.h>
 #include <string.h>
 
+#include "radiusd.h"
 #include "conf.h"
 #include "radpaths.h"
 #include "missing.h"
+#include "conffile.h"
 
 const char *progname;
 
+int debug_flag = 0;
+const char *radlog_dir = NULL;
+radlog_dest_t radlog_dest = RADLOG_FILES;
+const char *radutmp_file = NULL;
+
+int proxy_synchronous = TRUE;
+int proxy_fallback = FALSE;
+const char *radius_dir = NULL;
+const char *radacct_dir = NULL;
+const char *radlib_dir = NULL;
+int auth_port = 0;
+int acct_port;
+uint32_t myip = INADDR_ANY;
+int proxy_retry_delay = RETRY_DELAY;
+int proxy_retry_count = RETRY_COUNT;
+int proxy_dead_time;
+int log_stripped_names;
+struct main_config_t mainconfig;
+
 /*
  *     Possible states for request->state
  */
@@ -92,6 +115,12 @@ struct relay_misc {
        char            f_secret[256];                  /* File secret */
 };
 
+/*
+ * Used for reading the client configurations from the config files.
+ */
+char *c_secret = NULL;
+char *c_shortname = NULL;
+
 struct relay_request slots[NR_SLOTS];
 int request_head;
 int radius_id;
@@ -107,6 +136,7 @@ int do_recv(struct relay_misc *r_args);
 int do_send(struct relay_request *r, char *secret);
 int detail_move(char *from, char *to);
 void loop(struct relay_misc *r_args);
+int find_shortname(char *shortname, char **host, char **secret);
 void usage(void);
 
 
@@ -584,17 +614,66 @@ void loop(struct relay_misc *r_args)
        }
 }
 
+/*
+ * Search through the "client" config sections (usually in clients.conf).
+ * This is an easy way to find a secret and an host.
+ */
+int find_shortname(char *shortname, char **host, char **secret)
+{
+       CONF_SECTION *cs;
+
+       if (read_radius_conf_file() < 0) {
+               fprintf(stderr, "Error reading radiusd.conf\n");
+               exit(1);
+       }
+
+       /*
+        * Find the first 'client' section.
+        */
+       cs = cf_section_find("client");
+       if (cs) {
+               c_shortname = cf_section_value_find(cs, "shortname");
+               c_secret = cf_section_value_find(cs, "secret");
+               /*
+                * Keep searching for 'client' sections until they run out
+                * or we find one that matches.
+                */
+               while (cs && strcmp(shortname, c_shortname)) {
+                       free(c_shortname);
+                       free(c_secret);
+                       cs = cf_subsection_find_next(cs, cs, "client");
+                       if (cs) {
+                               c_shortname = cf_section_value_find(cs, "shortname");
+                               c_secret = cf_section_value_find(cs, "secret");
+                       }
+               };
+       };
+
+       if (cs) {
+               *host = cf_section_name2(cs);
+               *secret = c_secret;
+               if (host && secret)
+                       return 0;
+       }
+
+       return -1;
+}
+
 void usage(void)
 {
-       fprintf(stderr, "Usage: radrelay [-a accounting_dir] [-i local_ip] <[-s secret]\n");
-       fprintf(stderr, "[-S secret_file]> [-fx] remote-server[:port] detailfile\n");
-       fprintf(stderr, " -a accounting_dir     Base accounting directory\n");
-       fprintf(stderr, " -f                    Stay in the foreground (don't fork)\n");
-       fprintf(stderr, " -h                    This help\n");
-       fprintf(stderr, " -i local_ip           Use local_ip as source address\n");
-       fprintf(stderr, " -s secret             Server secret\n");
-       fprintf(stderr, " -S secret_file        Read server secret from file\n");
-       fprintf(stderr, " -x                    Debug mode\n");
+       fprintf(stderr, "Usage: radrelay [-a accounting_dir] [-d radius_dir] [-i local_ip] [-s secret]\n");
+       fprintf(stderr, "[-S secret_file] [-fx] <[-n shortname] [-r remote-server[:port]]> detailfile\n");
+       fprintf(stderr, " -a accounting_dir     Base accounting directory.\n");
+       fprintf(stderr, " -d radius_dir         Base radius (raddb) directory.\n");
+       fprintf(stderr, " -f                    Stay in the foreground (don't fork).\n");
+       fprintf(stderr, " -h                    This help.\n");
+       fprintf(stderr, " -i local_ip           Use local_ip as source address.\n");
+       fprintf(stderr, " -n shortname          Use the [shortname] entry from clients.conf for\n");
+       fprintf(stderr, "                       ip-adress and secret.\n");
+       fprintf(stderr, " -r remote-server      The destination address/hostname.\n");
+       fprintf(stderr, " -s secret             Server secret.\n");
+       fprintf(stderr, " -S secret_file        Read server secret from file.\n");
+       fprintf(stderr, " -x                    Debug mode (-xx gives more debugging).\n");
 
        exit(1);
 }
@@ -603,8 +682,8 @@ int main(int argc, char **argv)
 {
        struct servent *svp;
        char *server_name;
+       char *shortname;
        char *p;
-       const char *radius_dir = RADDBDIR;
        int c;
        int dontfork = 0;
        struct relay_misc r_args;
@@ -620,6 +699,11 @@ int main(int argc, char **argv)
        memset((char *) r_args.f_secret, 0, 256);
        r_args.secret = NULL;
 
+       shortname = NULL;
+       server_name = NULL;
+
+       radius_dir = strdup(RADIUS_DIR);
+
        librad_debug = 0;
 
        /*
@@ -631,7 +715,7 @@ int main(int argc, char **argv)
        /*
         *      Process the options.
         */
-       while ((c = getopt(argc, argv, "a:d:fhi:s:S:x")) != EOF) switch(c) {
+       while ((c = getopt(argc, argv, "a:d:fhi:n:r:s:S:x")) != EOF) switch(c) {
                case 'a':
                        if (strlen(optarg) > 1021) {
                                fprintf(stderr, "%s: acct_dir to long\n", progname);
@@ -640,21 +724,31 @@ int main(int argc, char **argv)
                        strncpy(r_args.detail, optarg, 1021);
                        break;
                case 'd':
-                       radius_dir = optarg;
+                       if (radius_dir)
+                               free(radius_dir);
+                       radius_dir = strdup(optarg);
                        break;
                case 'f':
                        dontfork = 1;
                        break;
+               case 'n':
+                       shortname = optarg;
+                       break;
+               case 'r':
+                       server_name = optarg;
+                       break;
                case 's':
                        r_args.secret = optarg;
                        break;
                case 'x':
                        /*
-                        * The variable debug should be set if we want radrelay internal
-                        * debugging, not used yet.
+                        * If -x is called once we enable internal radrelay
+                        * debugging, if it's called twice we also active
+                        * lib_rad debugging (fairly verbose).
                         */
+                       if (debug == 1)
+                               librad_debug = 1;
                        debug = 1;
-                       librad_debug = 1;
                        dontfork = 1;
                        break;
                case 'S':
@@ -700,12 +794,28 @@ int main(int argc, char **argv)
        }
        argc -= (optind - 1);
        argv += (optind - 1);
-       if (argc != 3) usage();
+       if (shortname && server_name)
+               usage();
+       if (!shortname && !server_name)
+               usage();
+       if (r_args.secret != NULL && shortname != NULL)
+               usage();
+
+       /*
+        * If we've been given a shortname, try to fetch the secret and
+        * adress from the config files.
+        */
+       if (shortname != NULL) {
+               if (find_shortname(shortname, &server_name, &r_args.secret) == -1) {
+                       fprintf(stderr, "Couldn't find %s in configuration files.\n", shortname);
+                       exit(1);
+               }
+       }
 
        /*
-        *      Find servers IP address and port.
+        * server_name should already be set either by the -r or the -s
+        * commandline argument.
         */
-       server_name = argv[1];
        if ((p = strrchr(server_name, ':')) != NULL) {
                *p = 0;
                p++;
@@ -717,7 +827,7 @@ int main(int argc, char **argv)
        } else {
                r_args.dst_port = ntohs(r_args.dst_port);
        }
-       r_args.dst_addr = ip_getaddr(argv[1]);
+       r_args.dst_addr = ip_getaddr(server_name);
        if (r_args.dst_addr == 0) {
                fprintf(stderr, "%s: unknown host\n",
                        server_name);
@@ -747,13 +857,14 @@ int main(int argc, char **argv)
                perror("chdir");
                exit(1);
        }
-       if (strlen(argv[2]) + strlen(r_args.detail) > 1023) {
+
+       if (strlen(argv[1]) + strlen(r_args.detail) > 1023) {
                fprintf(stderr, "Detail file path to long");
                exit(1);
        } else {
                if (r_args.detail[strlen(r_args.detail) - 1] != '/')
                        r_args.detail[strlen(r_args.detail)] = '/';
-               strncat (r_args.detail, argv[2], 1023 - strlen(r_args.detail));
+               strncat (r_args.detail, argv[1], 1023 - strlen(r_args.detail));
        }
 
        /*
index 8fad764..b1ee753 100644 (file)
@@ -25,6 +25,7 @@ static const char rcsid[] = "$Id$";
 #include       "libradius.h"
 
 #include       <sys/stat.h>
+#include       <sys/select.h>
 
 #include       <stdlib.h>
 #include       <string.h>
@@ -47,6 +48,9 @@ struct detail_instance {
 
        /* last made directory */
        char *last_made_directory;
+
+       /* if we want file locking */
+       int locking;
 };
 
 static CONF_PARSER module_config[] = {
@@ -56,6 +60,8 @@ static CONF_PARSER module_config[] = {
          offsetof(struct detail_instance,detailperm), NULL, "0600" },
        { "dirperm",       PW_TYPE_INTEGER,
          offsetof(struct detail_instance,dirperm),    NULL, "0755" },
+       { "locking",       PW_TYPE_BOOLEAN,
+         offsetof(struct detail_instance,locking),    NULL, "no" },
        { NULL, -1, 0, NULL, NULL }
 };
 
@@ -91,6 +97,11 @@ static int detail_accounting(void *instance, REQUEST *request)
        VALUE_PAIR      *pair;
        int             ret = RLM_MODULE_OK;
        struct stat     st;
+       int             locked;
+       int             lock_count;
+       struct timeval  tv;
+       REALM           *proxy_realm;
+       char            proxy_buffer[16];
 
        struct detail_instance *inst = instance;
 
@@ -153,20 +164,63 @@ static int detail_accounting(void *instance, REQUEST *request)
 
        /*
         *      Open & create the file, with the given permissions.
+        *
+        *      If we're not using locking, we'll just pass straight though
+        *      the while loop.
+        *      If we fail to aquire the filelock in 80 tries (approximately
+        *      two seconds) we bail out.
         */
-       if ((outfd = open(buffer, O_WRONLY|O_APPEND|O_CREAT,
-                         inst->detailperm)) < 0) {
-               radlog(L_ERR, "rlm_detail: Couldn't open file %s: %s",
-                      buffer, strerror(errno));
-               ret = RLM_MODULE_FAIL;
+       locked = 0;
+       lock_count = 0;
+       do {
+               if ((outfd = open(buffer, O_WRONLY | O_APPEND | O_CREAT,
+                                 inst->detailperm)) < 0) {
+                       radlog(L_ERR, "rlm_detail: Couldn't open file %s: %s",
+                              buffer, strerror(errno));
+                       ret = RLM_MODULE_FAIL;
+                       break;
+               }
+               if (inst->locking) {
+                       lseek(outfd, 0L, SEEK_SET);
+                       if (rad_lockfd_nonblock(outfd, 0) < 0) {
+                               close(outfd);
+                               tv.tv_sec = 0;
+                               tv.tv_usec = 25000;
+                               select(0, NULL, NULL, NULL, &tv);
+                               lock_count++;
+                       } else {
+                               DEBUG("rlm_detail: Aquired filelock, tried %d time(s)",
+                                     lock_count + 1);
+                               locked = 1;
+                       }
+               }
+       } while (!locked && inst->locking && lock_count < 80);
 
-       } else if ((outfp = fdopen(outfd, "a")) == NULL) {
-               radlog(L_ERR, "rlm_detail: Couldn't open file %s: %s",
-                      buffer, strerror(errno));
+       if (!locked && inst->locking && lock_count >= 80) {
+               radlog(L_ERR, "rlm_detail: Failed to aquire filelock for %s, giving up",
+                      buffer);
+               outfd = -1;
                ret = RLM_MODULE_FAIL;
-               close(outfd);
-       } else {
+       }
+
+       outfp = NULL;
+       if (outfd > -1) {
+               if ((outfp = fdopen(outfd, "a")) == NULL) {
+                       radlog(L_ERR, "rlm_detail: Couldn't open file %s: %s",
+                              buffer, strerror(errno));
+                       ret = RLM_MODULE_FAIL;
+                       if (inst->locking) {
+                               lseek(outfd, 0L, SEEK_SET);
+                               rad_unlockfd(outfd, 0);
+                               DEBUG("rlm_detail: Released filelock");
+                       }
+                       close(outfd);
+               }
+       }
+
+       if (outfd > -1 && outfp) {
                /* Post a timestamp */
+               fseek(outfp, 0L, SEEK_END);
                fputs(ctime_r(&request->timestamp, buffer), outfp);
 
                /* Write each attribute/value to the log file */
@@ -183,12 +237,34 @@ static int detail_accounting(void *instance, REQUEST *request)
                /*
                 *      Add non-protocol attibutes.
                 */
+               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);
+                               ip_ntoa(proxy_buffer, proxy_realm->acct_ipaddr);
+                               fprintf(outfp, "\tFreeradius-Proxied-To = %s\n",
+                                       proxy_buffer);
+                               DEBUG("rlm_detail: Freeradius-Proxied-To set to %s",
+                                     proxy_buffer);
+                       }
+               }
                fprintf(outfp, "\tTimestamp = %ld\n", 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);
+               pair = NULL;
+
                fputs("\n", outfp);
+
+               if (inst->locking) {
+                       fflush(outfp);
+                       lseek(outfd, 0L, SEEK_SET);
+                       rad_unlockfd(outfd, 0);
+                       DEBUG("rlm_detail: Released filelock");
+               }
+       
                fclose(outfp);
        }
 
index f895cdd..1ac4175 100644 (file)
@@ -239,6 +239,26 @@ static REALM *check_for_realm(void *instance, REQUEST *request)
                return NULL;
        }
 
+       /*
+        *      If this request has arrived from another freeradius server
+        *      that has already proxied the request, we don't need to do
+        *      it again.
+        */
+       for (vp = request->packet->vps; vp; vp = vp->next) {
+               if (vp->attribute == PW_FREERADIUS_PROXIED_TO) {
+                       if (request->packet->code == PW_AUTHENTICATION_REQUEST &&
+                           vp->lvalue == realm->ipaddr) {
+                               DEBUG2("rlm_realm: Request not proxied due to Freeradius-Proxied-To");
+                               return NULL;
+                       }
+                       if (request->packet->code == PW_ACCOUNTING_REQUEST &&
+                           vp->lvalue == realm->acct_ipaddr) {
+                               DEBUG2("rlm_realm: Request not proxied due to Freeradius-Proxied-To");
+                               return NULL;
+                       }
+               }
+        }
+
        return realm;
 }