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.
.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
.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.
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
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
}
/*
+ * 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.
*
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
#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>
#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
*/
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;
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);
}
}
+/*
+ * 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);
}
{
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;
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;
/*
/*
* 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);
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':
}
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++;
} 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);
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));
}
/*
#include "libradius.h"
#include <sys/stat.h>
+#include <sys/select.h>
#include <stdlib.h>
#include <string.h>
/* last made directory */
char *last_made_directory;
+
+ /* if we want file locking */
+ int locking;
};
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 }
};
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;
/*
* 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 */
/*
* 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);
}
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;
}