summaryrefslogtreecommitdiffstats
path: root/src/main
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-08-26 10:41:52 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-08-26 10:41:52 +0000
commitde8bf9112695763664912e340b265fa898188460 (patch)
tree9bcd5f8d45fc3b81174d3de8abfd573b68e9d7f6 /src/main
parentAdding debian version 3.2.3+dfsg-2. (diff)
downloadfreeradius-de8bf9112695763664912e340b265fa898188460.tar.xz
freeradius-de8bf9112695763664912e340b265fa898188460.zip
Merging upstream version 3.2.5+dfsg.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/main')
-rw-r--r--src/main/all.mk2
-rw-r--r--src/main/auth.c10
-rw-r--r--src/main/cb.c19
-rw-r--r--src/main/client.c44
-rw-r--r--src/main/command.c2
-rw-r--r--src/main/conffile.c62
-rw-r--r--src/main/detail.c10
-rw-r--r--src/main/exfile.c15
-rw-r--r--src/main/listen.c441
-rw-r--r--src/main/mainconfig.c104
-rw-r--r--src/main/map.c2
-rw-r--r--src/main/modcall.c2
-rw-r--r--src/main/modules.c6
-rw-r--r--src/main/process.c189
-rw-r--r--src/main/radclient.c205
-rwxr-xr-xsrc/main/radsecret7
-rw-r--r--src/main/radsecret.mk5
-rw-r--r--src/main/radsniff.c39
-rw-r--r--src/main/realms.c25
-rw-r--r--src/main/state.c113
-rw-r--r--src/main/stats.c95
-rw-r--r--src/main/threads.c2
-rw-r--r--src/main/tls.c137
-rw-r--r--src/main/tls_listen.c15
-rw-r--r--src/main/tmpl.c11
-rw-r--r--src/main/unittest.c2
-rw-r--r--src/main/util.c6
-rw-r--r--src/main/version.c2
28 files changed, 1259 insertions, 313 deletions
diff --git a/src/main/all.mk b/src/main/all.mk
index 2517cd2..f3db386 100644
--- a/src/main/all.mk
+++ b/src/main/all.mk
@@ -1,3 +1,3 @@
SUBMAKEFILES := radclient.mk radiusd.mk radsniff.mk radmin.mk radattr.mk \
- radwho.mk radlast.mk radtest.mk radzap.mk checkrad.mk \
+ radwho.mk radlast.mk radtest.mk radzap.mk checkrad.mk radsecret.mk \
libfreeradius-server.mk unittest.mk
diff --git a/src/main/auth.c b/src/main/auth.c
index 84889b8..2dc3e60 100644
--- a/src/main/auth.c
+++ b/src/main/auth.c
@@ -850,8 +850,8 @@ int rad_virtual_server(REQUEST *request)
break;
case PW_AUTH_TYPE_REJECT:
- request->reply->code = PW_CODE_ACCESS_REJECT;
- break;
+ request->reply->code = PW_CODE_ACCESS_REJECT;
+ break;
default:
break;
@@ -864,6 +864,12 @@ int rad_virtual_server(REQUEST *request)
if (vp) rad_postauth(request);
}
+ if (request->reply->code == PW_CODE_ACCESS_CHALLENGE) {
+ fr_pair_delete_by_num(&request->config, PW_POST_AUTH_TYPE, 0, TAG_ANY);
+ vp = pair_make_config("Post-Auth-Type", "Challenge", T_OP_SET);
+ if (vp) rad_postauth(request);
+ }
+
if (request->reply->code == PW_CODE_ACCESS_ACCEPT) {
/*
* Check that there is a name which can be used
diff --git a/src/main/cb.c b/src/main/cb.c
index db764aa..65e484f 100644
--- a/src/main/cb.c
+++ b/src/main/cb.c
@@ -31,6 +31,7 @@ void cbtls_info(SSL const *s, int where, int ret)
{
char const *role, *state;
REQUEST *request = SSL_get_ex_data(s, FR_TLS_EX_INDEX_REQUEST);
+ fr_tls_server_conf_t *conf = (fr_tls_server_conf_t *) SSL_get_ex_data(s, FR_TLS_EX_INDEX_CONF);
if ((where & ~SSL_ST_MASK) & SSL_ST_CONNECT) {
role = "Client ";
@@ -58,7 +59,7 @@ void cbtls_info(SSL const *s, int where, int ret)
len = strlen(abbrv);
if ((len > 1) && (abbrv[len - 1] == ' ')) len--;
- RDEBUG3("(TLS) Handshake state [%.*s] - %s%s (%d)",
+ RDEBUG3("(TLS) %s - Handshake state [%.*s] - %s%s (%d)", conf->name,
(int)len, abbrv, role, state, SSL_get_state(s));
/*
@@ -82,7 +83,7 @@ void cbtls_info(SSL const *s, int where, int ret)
client_ciphers = SSL_get_client_ciphers(s);
if (client_ciphers) {
- RDEBUG3("Client preferred ciphers (by priority)");
+ RDEBUG3("(TLS) %s - Client preferred ciphers (by priority)", conf->name);
num_ciphers = sk_SSL_CIPHER_num(client_ciphers);
for (i = 0; i < num_ciphers; i++) {
this_cipher = sk_SSL_CIPHER_value(client_ciphers, i);
@@ -92,7 +93,7 @@ void cbtls_info(SSL const *s, int where, int ret)
}
#endif
} else {
- RDEBUG2("(TLS) Handshake state - %s%s", role, state);
+ RDEBUG2("(TLS) %s - Handshake state - %s%s", conf->name, role, state);
}
return;
}
@@ -100,23 +101,27 @@ void cbtls_info(SSL const *s, int where, int ret)
if (where & SSL_CB_ALERT) {
if ((ret & 0xff) == SSL_AD_CLOSE_NOTIFY) return;
- RERROR("(TLS) Alert %s:%s:%s", (where & SSL_CB_READ) ? "read": "write",
+ RERROR("(TLS) %s - Alert %s:%s:%s", conf->name, (where & SSL_CB_READ) ? "read": "write",
SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret));
return;
}
if (where & SSL_CB_EXIT) {
if (ret == 0) {
- RERROR("(TLS) %s: Failed in %s", role, state);
+ RERROR("(TLS) %s - %s: Failed in %s", conf->name, role, state);
return;
}
if (ret < 0) {
if (SSL_want_read(s)) {
- RDEBUG2("(TLS) %s: Need to read more data: %s", role, state);
+ RDEBUG2("(TLS) %s - %s: Need to read more data: %s", conf->name, role, state);
return;
}
- RERROR("(TLS) %s: Error in %s", role, state);
+ if (SSL_want_write(s)) {
+ RDEBUG2("(TLS) %s - %s: Need to write more data: %s", conf->name, role, state);
+ return;
+ }
+ RERROR("(TLS) %s - %s: Error in %s", conf->name, role, state);
}
}
}
diff --git a/src/main/client.c b/src/main/client.c
index 12f7824..58f9faa 100644
--- a/src/main/client.c
+++ b/src/main/client.c
@@ -328,7 +328,8 @@ check_list:
(old->coa_home_server == client->coa_home_server) &&
(old->coa_home_pool == client->coa_home_pool) &&
#endif
- (old->message_authenticator == client->message_authenticator)) {
+ (old->require_ma == client->require_ma) &&
+ (old->limit_proxy_state == client->limit_proxy_state)) {
WARN("Ignoring duplicate client %s", client->longname);
client_free(client);
return true;
@@ -490,6 +491,8 @@ static fr_ipaddr_t cl_ipaddr;
static uint32_t cl_netmask;
static char const *cl_srcipaddr = NULL;
static char const *hs_proto = NULL;
+static char const *require_message_authenticator = NULL;
+static char const *limit_proxy_state = NULL;
#ifdef WITH_TCP
static CONF_PARSER limit_config[] = {
@@ -512,7 +515,8 @@ static const CONF_PARSER client_config[] = {
{ "src_ipaddr", FR_CONF_POINTER(PW_TYPE_STRING, &cl_srcipaddr), NULL },
- { "require_message_authenticator", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, message_authenticator), "no" },
+ { "require_message_authenticator", FR_CONF_POINTER(PW_TYPE_STRING| PW_TYPE_IGNORE_DEFAULT, &require_message_authenticator), NULL },
+ { "limit_proxy_state", FR_CONF_POINTER(PW_TYPE_STRING| PW_TYPE_IGNORE_DEFAULT, &limit_proxy_state), NULL },
{ "secret", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, RADCLIENT, secret), NULL },
{ "shortname", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, shortname), NULL },
@@ -724,7 +728,7 @@ static const CONF_PARSER dynamic_config[] = {
{ "FreeRADIUS-Client-Src-IP-Address", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, RADCLIENT, src_ipaddr), NULL },
{ "FreeRADIUS-Client-Src-IPv6-Address", FR_CONF_OFFSET(PW_TYPE_IPV6_ADDR, RADCLIENT, src_ipaddr), NULL },
- { "FreeRADIUS-Client-Require-MA", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, message_authenticator), NULL },
+ { "FreeRADIUS-Client-Require-MA", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, dynamic_require_ma), NULL },
{ "FreeRADIUS-Client-Secret", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, secret), "" },
{ "FreeRADIUS-Client-Shortname", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, shortname), "" },
@@ -906,8 +910,19 @@ RADCLIENT *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bo
c = talloc_zero(ctx, RADCLIENT);
c->cs = cs;
+ /*
+ * Set the "require message authenticator" and "limit
+ * proxy state" flags from the global default. If the
+ * configuration item exists, AND is set, it will
+ * over-ride the flag.
+ */
+ c->require_ma = main_config.require_ma;
+ c->limit_proxy_state = main_config.limit_proxy_state;
+
memset(&cl_ipaddr, 0, sizeof(cl_ipaddr));
cl_netmask = 255;
+ require_message_authenticator = NULL;
+ limit_proxy_state = NULL;
if (cf_section_parse(cs, c, client_config) < 0) {
cf_log_err_cs(cs, "Error parsing client section");
@@ -917,6 +932,8 @@ RADCLIENT *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bo
hs_proto = NULL;
cl_srcipaddr = NULL;
#endif
+ require_message_authenticator = NULL;
+ limit_proxy_state = NULL;
return NULL;
}
@@ -1189,6 +1206,16 @@ done_coa:
}
#endif
+ if (fr_bool_auto_parse(cf_pair_find(cs, "require_message_authenticator"), &c->require_ma, require_message_authenticator) < 0) {
+ goto error;
+ }
+
+ if (c->require_ma != FR_BOOL_TRUE) {
+ if (fr_bool_auto_parse(cf_pair_find(cs, "limit_proxy_state"), &c->limit_proxy_state, limit_proxy_state) < 0) {
+ goto error;
+ }
+ }
+
return c;
}
@@ -1233,7 +1260,7 @@ RADCLIENT *client_afrom_query(TALLOC_CTX *ctx, char const *identifier, char cons
if (shortname) c->shortname = talloc_typed_strdup(c, shortname);
if (type) c->nas_type = talloc_typed_strdup(c, type);
if (server) c->server = talloc_typed_strdup(c, server);
- c->message_authenticator = require_ma;
+ c->require_ma = require_ma;
return c;
}
@@ -1419,10 +1446,10 @@ RADCLIENT *client_afrom_request(RADCLIENT_LIST *clients, REQUEST *request)
*pi = vp->vp_integer;
/*
- * Same nastiness as above.
+ * Same nastiness as above, but hard-coded for require Message-Authenticator.
*/
for (parse = client_config; parse->name; parse++) {
- if (parse->offset == dynamic_config[i].offset) break;
+ if (parse->type == PW_TYPE_BOOLEAN) break;
}
if (!parse) break;
@@ -1513,6 +1540,11 @@ validate:
goto error;
}
+ /*
+ * It can't be set to "auto". Too bad.
+ */
+ c->require_ma = (fr_bool_auto_t) c->dynamic_require_ma;
+
if (!client_add_dynamic(clients, request->client, c)) {
return NULL;
}
diff --git a/src/main/command.c b/src/main/command.c
index 988f43b..266366b 100644
--- a/src/main/command.c
+++ b/src/main/command.c
@@ -2701,7 +2701,7 @@ static int command_stats_socket(rad_listen_t *listener, int argc, char *argv[])
return command_print_stats(listener, &sock->stats, auth, 0);
}
-static int command_stats_pool(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[])
+static int command_stats_pool(rad_listen_t *listener, int argc, char *argv[])
{
CONF_SECTION *cs;
module_instance_t *mi;
diff --git a/src/main/conffile.c b/src/main/conffile.c
index 7bb206c..ad5a5fe 100644
--- a/src/main/conffile.c
+++ b/src/main/conffile.c
@@ -1424,12 +1424,13 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
{
int rcode;
bool deprecated, required, attribute, secret, file_input, cant_be_empty, tmpl, multi, file_exists;
+ bool ignore_dflt;
char **q;
char const *value;
CONF_PAIR *cp = NULL;
fr_ipaddr_t *ipaddr;
- char buffer[8192];
CONF_ITEM *c_item;
+ char buffer[8192];
if (!cs) {
cf_log_err(&(cs->item), "No enclosing section for configuration item \"%s\"", name);
@@ -1447,6 +1448,7 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
cant_be_empty = (type & PW_TYPE_NOT_EMPTY);
tmpl = (type & PW_TYPE_TMPL);
multi = (type & PW_TYPE_MULTI);
+ ignore_dflt = (type & PW_TYPE_IGNORE_DEFAULT);
if (attribute) required = true;
if (required) cant_be_empty = true; /* May want to review this in the future... */
@@ -1470,7 +1472,7 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
* section, use the default value.
*/
if (!cp) {
- if (deprecated) return 0; /* Don't set the default value */
+ if (deprecated || ignore_dflt) return 0; /* Don't set the default value */
rcode = 1;
value = dflt;
@@ -1658,6 +1660,62 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d
cf_log_err(&(cs->item),"Failed expanding variable %s", name);
return -1;
}
+
+ } else if (cf_new_escape && (cp->rhs_type == T_DOUBLE_QUOTED_STRING) && (strchr(value, '\\') != NULL)) {
+ char const *p = value;
+ char *s = buffer;
+ char *end = buffer + sizeof(buffer);
+ unsigned int x;
+
+ /*
+ * We pass !cf_new_escape() to gettoken() when we parse the RHS of a CONF_PAIR
+ * above. But gettoken() unescapes the \", and doesn't unescape anything else.
+ * So we do it here.
+ */
+ while (*p && (s < end)) {
+ if (*p != '\\') {
+ *(s++) = *(p++);
+ continue;
+ }
+
+ p++;
+
+ switch (*p) {
+ case 'r':
+ *s++ = '\r';
+ break;
+ case 'n':
+ *s++ = '\n';
+ break;
+ case 't':
+ *s++ = '\t';
+ break;
+
+ default:
+ if (*p >= '0' && *p <= '9' &&
+ sscanf(p, "%3o", &x) == 1) {
+ if (!x) {
+ cf_log_err(&(cs->item), "Cannot have embedded zeros in value for %s", name);
+ return -1;
+ }
+
+ *s++ = x;
+ p += 2;
+ } else
+ *s++ = *p;
+ break;
+ }
+ p++;
+ }
+
+ if (s == end) {
+ cf_log_err(&(cs->item), "Failed expanding value for %s", name);
+ return -1;
+ }
+
+ *s = '\0';
+
+ value = buffer;
}
if (cant_be_empty && (value[0] == '\0')) goto cant_be_empty;
diff --git a/src/main/detail.c b/src/main/detail.c
index a5e8437..3b7e382 100644
--- a/src/main/detail.c
+++ b/src/main/detail.c
@@ -910,8 +910,16 @@ open_file:
rad_assert(vp != NULL);
fr_pair_add(&packet->vps, vp);
}
+
+ /*
+ * Update Acct-Delay-Time, but make sure that it doesn't go backwards.
+ */
if (data->timestamp != 0) {
- vp->vp_integer += time(NULL) - data->timestamp;
+ time_t now = time(NULL);
+
+ if (((time_t) data->timestamp) < now) {
+ vp->vp_integer += time(NULL) - data->timestamp;
+ }
}
}
diff --git a/src/main/exfile.c b/src/main/exfile.c
index 59e6a05..1b498ce 100644
--- a/src/main/exfile.c
+++ b/src/main/exfile.c
@@ -170,7 +170,20 @@ static int exfile_open_mkdir(exfile_t *ef, char const *filename, mode_t permissi
oflag = O_RDWR;
}
- fd = open(filename, oflag, permissions);
+ /*
+ * Just dup stdout / stderr if it's possible.
+ */
+ if ((default_log.dst == L_DST_STDOUT) &&
+ (strcmp(filename, "/dev/stdout") == 0)) {
+ fd = dup(STDOUT_FILENO);
+
+ } else if ((default_log.dst == L_DST_STDERR) &&
+ (strcmp(filename, "/dev/stderr") == 0)) {
+ fd = dup(STDERR_FILENO);
+ } else {
+ fd = open(filename, oflag, permissions);
+ }
+
if (fd < 0) {
fr_strerror_printf("Failed to open file %s: %s",
filename, strerror(errno));
diff --git a/src/main/listen.c b/src/main/listen.c
index ee73a57..a6ff080 100644
--- a/src/main/listen.c
+++ b/src/main/listen.c
@@ -55,7 +55,7 @@ RCSID("$Id$")
#ifdef WITH_TLS
#include <netinet/tcp.h>
-# ifdef __APPLE__
+# if defined(__APPLE__) || defined(__FreeBSD__) || defined(__illumos__) || defined(__sun__)
# if !defined(SOL_TCP) && defined(IPPROTO_TCP)
# define SOL_TCP IPPROTO_TCP
# endif
@@ -385,6 +385,7 @@ int rad_status_server(REQUEST *request)
if (sock->state == LISTEN_TLS_CHECKING) {
int autz_type = PW_AUTZ_TYPE;
char const *name = "Autz-Type";
+ rad_listen_t *listener = request->listener;
if (request->listener->type == RAD_LISTEN_ACCT) {
autz_type = PW_ACCT_TYPE;
@@ -404,11 +405,22 @@ int rad_status_server(REQUEST *request)
if ((rcode == RLM_MODULE_OK) || (rcode == RLM_MODULE_UPDATED)) {
RDEBUG("(TLS) Connection is authorized");
request->reply->code = PW_CODE_ACCESS_ACCEPT;
+
+ listener->status = RAD_LISTEN_STATUS_RESUME;
+
+ rad_assert(sock->request->packet != request->packet);
+
+ sock->state = LISTEN_TLS_SETUP;
+
} else {
RWDEBUG("(TLS) Connection is not authorized - closing TCP socket.");
request->reply->code = PW_CODE_ACCESS_REJECT;
+
+ listener->status = RAD_LISTEN_STATUS_EOL;
+ listener->tls = NULL; /* parent owns this! */
}
+ radius_update_listener(listener);
return 0;
}
}
@@ -518,6 +530,123 @@ int rad_status_server(REQUEST *request)
return 0;
}
+static void blastradius_checks(RADIUS_PACKET *packet, RADCLIENT *client)
+{
+ if (client->require_ma == FR_BOOL_TRUE) return;
+
+ if (client->require_ma == FR_BOOL_AUTO) {
+ if (!packet->message_authenticator) {
+ ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ ERROR("BlastRADIUS check: Received packet without Message-Authenticator.");
+ ERROR("Setting \"require_message_authenticator = false\" for client %s", client->shortname);
+ ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ ERROR("UPGRADE THE CLIENT AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK.");
+ ERROR("Once the client is upgraded, set \"require_message_authenticator = true\" for client %s", client->shortname);
+ ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ client->require_ma = FR_BOOL_FALSE;
+
+ /*
+ * And fall through to the
+ * limit_proxy_state checks, which might
+ * complain again. Oh well, maybe that
+ * will make people read the messages.
+ */
+
+ } else if (packet->eap_message) {
+ /*
+ * Don't set it to "true" for packets
+ * with EAP-Message. It's already
+ * required there, and we might get a
+ * non-EAP packet with (or without)
+ * Message-Authenticator
+ */
+ return;
+ } else {
+ ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ ERROR("BlastRADIUS check: Received packet with Message-Authenticator.");
+ ERROR("Setting \"require_message_authenticator = true\" for client %s", client->shortname);
+ ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ ERROR("It looks like the client has been updated to protect from the BlastRADIUS attack.");
+ ERROR("Please set \"require_message_authenticator = true\" for client %s", client->shortname);
+ ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+
+ client->require_ma = FR_BOOL_TRUE;
+ return;
+ }
+
+ }
+
+ /*
+ * If all of the checks are turned off, then complain for every packet we receive.
+ */
+ if (client->limit_proxy_state == FR_BOOL_FALSE) {
+ /*
+ * We have a Message-Authenticator, and it's valid. We don't need to compain.
+ */
+ if (packet->message_authenticator) return;
+
+ if (!fr_debug_lvl) return; /* easier than checking for each line below */
+
+ DEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ DEBUG("BlastRADIUS check: Received packet without Message-Authenticator.");
+ DEBUG("YOU MUST SET \"require_message_authenticator = true\", or");
+ DEBUG("YOU MUST SET \"limit_proxy_state = true\" for client %s", client->shortname);
+ DEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ DEBUG("The packet does not contain Message-Authenticator, which is a security issue");
+ DEBUG("UPGRADE THE CLIENT AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK.");
+ DEBUG("Once the client is upgraded, set \"require_message_authenticator = true\" for client %s", client->shortname);
+ DEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ return;
+ }
+
+ /*
+ * Don't complain here. rad_packet_ok() will instead
+ * complain about every packet with Proxy-State but which
+ * is missing Message-Authenticator.
+ */
+ if (client->limit_proxy_state == FR_BOOL_TRUE) {
+ return;
+ }
+
+ if (packet->proxy_state && !packet->message_authenticator) {
+ ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ ERROR("BlastRADIUS check: Received packet with Proxy-State, but without Message-Authenticator.");
+ ERROR("This is either a BlastRADIUS attack, OR");
+ ERROR("the client is a proxy RADIUS server which has not been upgraded.");
+ ERROR("Setting \"limit_proxy_state = false\" for client %s", client->shortname);
+ ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ ERROR("UPGRADE THE CLIENT AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK.");
+ ERROR("Once the client is upgraded, set \"require_message_authenticator = true\" for client %s", client->shortname);
+ ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+
+ client->limit_proxy_state = FR_BOOL_FALSE;
+
+ } else {
+ client->limit_proxy_state = FR_BOOL_TRUE;
+
+ ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ if (!packet->proxy_state) {
+ ERROR("BlastRADIUS check: Received packet without Proxy-State.");
+ } else {
+ ERROR("BlastRADIUS check: Received packet with Proxy-State and Message-Authenticator.");
+ }
+
+ ERROR("Setting \"limit_proxy_state = true\" for client %s", client->shortname);
+ ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+
+ if (!packet->message_authenticator) {
+ ERROR("The packet does not contain Message-Authenticator, which is a security issue.");
+ ERROR("UPGRADE THE CLIENT AS YOUR NETWORK MAY BE VULNERABLE TO THE BLASTRADIUS ATTACK.");
+ ERROR("Once the client is upgraded, set \"require_message_authenticator = true\" for client %s", client->shortname);
+ } else {
+ ERROR("The packet contains Message-Authenticator.");
+ if (!packet->eap_message) ERROR("The client has likely been upgraded to protect from the attack.");
+ ERROR("Please set \"require_message_authenticator = true\" for client %s", client->shortname);
+ }
+ ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ }
+}
+
#ifdef WITH_TCP
static int dual_tcp_recv(rad_listen_t *listener)
{
@@ -594,6 +723,21 @@ static int dual_tcp_recv(rad_listen_t *listener)
switch (packet->code) {
case PW_CODE_ACCESS_REQUEST:
if (listener->type != RAD_LISTEN_AUTH) goto bad_packet;
+
+ /*
+ * Enforce BlastRADIUS checks on TCP, too.
+ */
+ if (!rad_packet_ok(packet, (client->require_ma == FR_BOOL_TRUE) | ((client->limit_proxy_state == FR_BOOL_TRUE) << 2), NULL)) {
+ FR_STATS_INC(auth, total_malformed_requests);
+ rad_free(&sock->packet);
+ return 0;
+ }
+
+ /*
+ * Perform BlastRADIUS checks and warnings.
+ */
+ if (packet->code == PW_CODE_ACCESS_REQUEST) blastradius_checks(packet, client);
+
FR_STATS_INC(auth, total_requests);
fun = rad_authenticate;
break;
@@ -720,7 +864,12 @@ static int tls_sni_callback(SSL *ssl, UNUSED int *al, void *arg)
#endif
#ifdef WITH_RADIUSV11
-static const unsigned char radiusv11_alpn_protos[] = {
+static const unsigned char radiusv11_allow_protos[] = {
+ 10, 'r', 'a', 'd', 'i', 'u', 's', '/', '1', '.', '1', /* prefer this */
+ 10, 'r', 'a', 'd', 'i', 'u', 's', '/', '1', '.', '0',
+};
+
+static const unsigned char radiusv11_require_protos[] = {
10, 'r', 'a', 'd', 'i', 'u', 's', '/', '1', '.', '1',
};
@@ -750,23 +899,28 @@ static int radiusv11_server_alpn_cb(SSL *ssl,
memcpy(&hack, &out, sizeof(out)); /* const issues */
/*
- * The RADIUSv11 configuration for this socket is a combination of what we require, and what we
+ * The RADIUS/1.1 configuration for this socket is a combination of what we require, and what we
* require of the client.
*/
switch (this->radiusv11) {
/*
- * If we forbid RADIUSv11, then we never advertised it via ALPN, and this callback should
+ * If we forbid RADIUS/1.1, then we never advertised it via ALPN, and this callback should
* never have been registered.
*/
case FR_RADIUSV11_FORBID:
- *out = NULL;
- *outlen = 0;
- return SSL_TLSEXT_ERR_OK;
+ fr_assert(0);
+ server = radiusv11_allow_protos + 11;
+ server_len = 11;
+ break;
case FR_RADIUSV11_ALLOW:
+ server = radiusv11_allow_protos;
+ server_len = sizeof(radiusv11_allow_protos);
+ break;
+
case FR_RADIUSV11_REQUIRE:
- server = radiusv11_alpn_protos;
- server_len = sizeof(radiusv11_alpn_protos);
+ server = radiusv11_require_protos;
+ server_len = sizeof(radiusv11_require_protos);
break;
}
@@ -786,6 +940,7 @@ static int radiusv11_server_alpn_cb(SSL *ssl,
*/
fr_assert(*outlen == 10);
sock->radiusv11 = (server[9] == '1');
+ sock->alpn_checked = true;
RDEBUG("(TLS) ALPN server negotiated application protocol \"%.*s\"", (int) *outlen, server);
return SSL_TLSEXT_ERR_OK;
@@ -798,6 +953,26 @@ static int radiusv11_server_alpn_cb(SSL *ssl,
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
+static int radiusv11_client_hello_cb(UNUSED SSL *s, int *alert, void *arg)
+{
+ rad_listen_t *this = arg;
+ listen_socket_t *sock = this->data;
+
+ /*
+ * The server_alpn_cb ran, and checked that the configured ALPN matches the negotiated one.
+ */
+ if (sock->alpn_checked) return SSL_CLIENT_HELLO_SUCCESS;
+
+ /*
+ * The server_alpn_cb did NOT run (???) but we still have a client hello. We require ALPN and
+ * none was negotiated, so we return an error.
+ */
+ *alert = SSL_AD_NO_APPLICATION_PROTOCOL;
+
+ return SSL_CLIENT_HELLO_ERROR;
+}
+
+
int fr_radiusv11_client_init(fr_tls_server_conf_t *tls);
int fr_radiusv11_client_get_alpn(rad_listen_t *listener);
@@ -805,13 +980,17 @@ int fr_radiusv11_client_init(fr_tls_server_conf_t *tls)
{
switch (tls->radiusv11) {
case FR_RADIUSV11_ALLOW:
- case FR_RADIUSV11_REQUIRE:
- if (SSL_CTX_set_alpn_protos(tls->ctx, radiusv11_alpn_protos, sizeof(radiusv11_alpn_protos)) != 0) {
- ERROR("Failed setting RADIUSv11 negotiation flags");
+ if (SSL_CTX_set_alpn_protos(tls->ctx, radiusv11_allow_protos, sizeof(radiusv11_allow_protos)) != 0) {
+ fail_protos:
+ ERROR("Failed setting RADIUS/1.1 negotiation flags");
return -1;
}
break;
+ case FR_RADIUSV11_REQUIRE:
+ if (SSL_CTX_set_alpn_protos(tls->ctx, radiusv11_require_protos, sizeof(radiusv11_require_protos)) != 0) goto fail_protos;
+ break;
+
default:
break;
}
@@ -827,41 +1006,53 @@ int fr_radiusv11_client_get_alpn(rad_listen_t *listener)
SSL_get0_alpn_selected(sock->ssn->ssl, &data, &len);
if (!data) {
- DEBUG("(TLS) ALPN home server did not send any application protocol");
+ DEBUG("(TLS) ALPN server did not send any application protocol");
if (listener->radiusv11 == FR_RADIUSV11_REQUIRE) {
DEBUG("(TLS) We have 'radiusv11 = require', but the home server has not negotiated it - closing socket");
return -1;
}
- DEBUG("(TLS) ALPN assuming historical RADIUS");
- return 0;
+ DEBUG("(TLS) ALPN assuming \"radius/1.0\"");
+ return 0; /* allow radius/1.0 */
}
- DEBUG("(TLS) ALPN home server sent application protocol \"%.*s\"", (int) len, data);
+ DEBUG("(TLS) ALPN server sent application protocol \"%.*s\"", (int) len, data);
if (len != 10) {
radiusv11_unknown:
- DEBUG("(TLS) ALPN home server sent unknown application protocol - closing connection");
+ DEBUG("(TLS) ALPN server sent unknown application protocol - closing connection to home server");
return -1;
}
/*
- * Should always be "radius/1.1". The server MUST echo back one of the strings
+ * Should always be "radius/1.0" or "radius/1.1". The server MUST echo back one of the strings
* we sent. If it doesn't, it's a bad server.
*/
- if (memcmp(data, "radius/1.1", 10) != 0) goto radiusv11_unknown;
+ if (memcmp(data, "radius/1.", 9) != 0) goto radiusv11_unknown;
+
+ if ((data[9] != '0') && (data[9] != '1')) goto radiusv11_unknown;
/*
* Double-check what the server sent us. It SHOULD be sane, but it never hurts to check.
*/
switch (listener->radiusv11) {
case FR_RADIUSV11_FORBID:
- DEBUG("(TLS) ALPN home server sent \"radius/v1.1\" but we forbid it - closing connection to home server");
- return -1;
+ if (data[9] != '0') {
+ DEBUG("(TLS) ALPN server did not send \"radius/v1.0\" - closing connection to home server");
+ return -1;
+ }
+ break;
case FR_RADIUSV11_ALLOW:
+ sock->radiusv11 = (data[9] == '1');
+ break;
+
case FR_RADIUSV11_REQUIRE:
- DEBUG("(TLS) ALPN using \"radius/1.1\"");
+ if (data[9] != '1') {
+ DEBUG("(TLS) ALPN server did not send \"radius/v1.1\" - closing connection to home server");
+ return -1;
+ }
+
sock->radiusv11 = true;
break;
}
@@ -1066,15 +1257,30 @@ static int dual_tcp_accept(rad_listen_t *listener)
SSL_CTX_set_tlsext_servername_callback(this->tls->ctx, tls_sni_callback);
SSL_CTX_set_tlsext_servername_arg(this->tls->ctx, this->tls);
#ifdef WITH_RADIUSV11
- /*
- * Default is "forbid" (0). In which case we don't set any ALPN callbacks, and
- * the ServerHello does not contain an ALPN section.
- */
- if (client->radiusv11 != FR_RADIUSV11_FORBID) {
+ switch (client->radiusv11) {
+ /*
+ * We don't set any callbacks. If the client sends ALPN (or not), we
+ * just do normal RADIUS.
+ */
+ case FR_RADIUSV11_FORBID:
+ DEBUG("(TLS) ALPN radiusv11 = forbid");
+ break;
+
+ /*
+ * Setting the client hello callback catches the case where we send ALPN,
+ * and the client doesn't send anything.
+ */
+ case FR_RADIUSV11_REQUIRE:
+ SSL_CTX_set_client_hello_cb(this->tls->ctx, radiusv11_client_hello_cb, this);
+ /* FALL-THROUGH */
+
+ /*
+ * We're willing to do normal RADIUS, but we send ALPN, and then check if
+ * (or what) the client sends back as ALPN.
+ */
+ case FR_RADIUSV11_ALLOW:
SSL_CTX_set_alpn_select_cb(this->tls->ctx, radiusv11_server_alpn_cb, this);
DEBUG("(TLS) ALPN radiusv11 = allow / require");
- } else {
- DEBUG("(TLS) ALPN radiusv11 = forbid");
}
#endif
}
@@ -1313,6 +1519,12 @@ static CONF_PARSER limit_config[] = {
{ "max_connections", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_socket_t, limit.max_connections), "16" },
{ "lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_socket_t, limit.lifetime), "0" },
{ "idle_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_socket_t, limit.idle_timeout), STRINGIFY(30) },
+#ifdef SO_RCVTIMEO
+ { "read_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_socket_t, limit.read_timeout), NULL },
+#endif
+#ifdef SO_SNDTIMEO
+ { "write_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_socket_t, limit.write_timeout), NULL },
+#endif
#endif
CONF_PARSER_TERMINATOR
};
@@ -1467,6 +1679,8 @@ int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this)
return -1;
}
+ this->tls->name = "RADIUS/TLS";
+
#ifdef HAVE_PTHREAD_H
if (pthread_mutex_init(&sock->mutex, NULL) < 0) {
rad_assert(0 == 1);
@@ -1822,8 +2036,6 @@ static int stats_socket_recv(rad_listen_t *listener)
rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code);
if (rcode < 0) return 0;
- FR_STATS_INC(auth, total_requests);
-
if (rcode < 20) { /* RADIUS_HDR_LEN */
if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror());
FR_STATS_INC(auth, total_malformed_requests);
@@ -1871,7 +2083,6 @@ static int stats_socket_recv(rad_listen_t *listener)
}
#endif
-
/*
* Check if an incoming request is "ok"
*
@@ -1947,7 +2158,7 @@ static int auth_socket_recv(rad_listen_t *listener)
* Now that we've sanity checked everything, receive the
* packet.
*/
- packet = rad_recv(ctx, listener->fd, client->message_authenticator);
+ packet = rad_recv(ctx, listener->fd, (client->require_ma == FR_BOOL_TRUE) | ((client->limit_proxy_state == FR_BOOL_TRUE) << 2));
if (!packet) {
FR_STATS_INC(auth, total_malformed_requests);
if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror());
@@ -1955,6 +2166,11 @@ static int auth_socket_recv(rad_listen_t *listener)
return 0;
}
+ /*
+ * Perform BlastRADIUS checks and warnings.
+ */
+ if (packet->code == PW_CODE_ACCESS_REQUEST) blastradius_checks(packet, client);
+
#ifdef __APPLE__
#ifdef WITH_UDPFROMTO
/*
@@ -2343,7 +2559,7 @@ static int coa_socket_recv(rad_listen_t *listener)
* Now that we've sanity checked everything, receive the
* packet.
*/
- packet = rad_recv(ctx, listener->fd, client->message_authenticator);
+ packet = rad_recv(ctx, listener->fd, client->require_ma);
if (!packet) {
FR_STATS_INC(coa, total_malformed_requests);
if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror());
@@ -2663,7 +2879,7 @@ static int proxy_socket_encode(RADIUSV11_UNUSED rad_listen_t *listener, REQUEST
}
-static int proxy_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request)
+static int proxy_socket_decode(RADIUSV11_UNUSED rad_listen_t *listener, REQUEST *request)
{
#ifdef WITH_RADIUSV11
listen_socket_t *sock = listener->data;
@@ -2907,6 +3123,9 @@ static int listen_bind(rad_listen_t *this)
*/
if (sock->interface) {
#ifdef SO_BINDTODEVICE
+ /*
+ * Linux: Bind to an interface by name.
+ */
struct ifreq ifreq;
memset(&ifreq, 0, sizeof(ifreq));
@@ -2919,45 +3138,81 @@ static int listen_bind(rad_listen_t *this)
if (rcode < 0) {
close(this->fd);
ERROR("Failed binding to interface %s: %s",
- sock->interface, fr_syserror(errno));
+ sock->interface, fr_syserror(errno));
return -1;
- } /* else it worked. */
+ }
#else
+
+ /*
+ * If we don't bind to an interface by name, we usually bind to it by index.
+ */
+ int idx = if_nametoindex(sock->interface);
+
+ if (idx == 0) {
+ close(this->fd);
+ ERROR("Failed finding interface %s: %s",
+ sock->interface, fr_syserror(errno));
+ return -1;
+ }
+
+#ifdef IP_BOUND_IF
+ /*
+ * OSX / ?BSD / Solaris: bind to interface by index for IPv4
+ */
+ if (sock->my_ipaddr.af == AF_INET) {
+ rad_suid_up();
+ rcode = setsockopt(this->fd, IPPROTO_IP, IP_BOUND_IF, &idx, sizeof(idx));
+ rad_suid_down();
+ if (rcode < 0) {
+ close(this->fd);
+ ERROR("Failed binding to interface %s: %s",
+ sock->interface, fr_syserror(errno));
+ return -1;
+ }
+ } else
+#endif
+
+#ifdef IPV6_BOUND_IF
+ /*
+ * OSX / ?BSD / Solaris: bind to interface by index for IPv6
+ */
+ if (sock->my_ipaddr.af == AF_INET6) {
+ rad_suid_up();
+ rcode = setsockopt(this->fd, IPPROTO_IPV6, IPV6_BOUND_IF, &idx, sizeof(idx));
+ rad_suid_down();
+ if (rcode < 0) {
+ close(this->fd);
+ ERROR("Failed binding to interface %s: %s",
+ sock->interface, fr_syserror(errno));
+ return -1;
+ }
+ } else
+#endif
+
#ifdef HAVE_STRUCT_SOCKADDR_IN6
#ifdef HAVE_NET_IF_H
/*
- * Odds are that any system supporting "bind to
- * device" also supports IPv6, so this next bit
- * isn't necessary. But it's here for
- * completeness.
- *
- * If we're doing IPv6, and the scope hasn't yet
- * been defined, set the scope to the scope of
- * the interface.
+ * Otherwise generic IPv6: set the scope to the
+ * interface, and hope that all of the read/write
+ * routines respect that.
*/
if (sock->my_ipaddr.af == AF_INET6) {
if (sock->my_ipaddr.scope == 0) {
- sock->my_ipaddr.scope = if_nametoindex(sock->interface);
- if (sock->my_ipaddr.scope == 0) {
- close(this->fd);
- ERROR("Failed finding interface %s: %s",
- sock->interface, fr_syserror(errno));
- return -1;
- }
- } /* else scope was defined: we're OK. */
+ sock->my_ipaddr.scope = idx;
+ } /* else scope was already defined */
} else
#endif
#endif
- /*
- * IPv4: no link local addresses,
- * and no bind to device.
- */
+
+ /*
+ * IPv4, or no socket options to bind to interface.
+ */
{
close(this->fd);
ERROR("Failed binding to interface %s: \"bind to device\" is unsupported", sock->interface);
return -1;
}
-#endif
+#endif /* SO_BINDTODEVICE */
}
#ifdef WITH_TCP
@@ -3067,6 +3322,7 @@ static int listen_bind(rad_listen_t *this)
int on = 1;
if (setsockopt(this->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
+ close(this->fd);
ERROR("Can't set broadcast option: %s",
fr_syserror(errno));
return -1;
@@ -3115,6 +3371,7 @@ static int listen_bind(rad_listen_t *this)
memset(&src, 0, sizeof_src);
if (getsockname(this->fd, (struct sockaddr *) &src,
&sizeof_src) < 0) {
+ close(this->fd);
ERROR("Failed getting socket name: %s",
fr_syserror(errno));
return -1;
@@ -3122,6 +3379,7 @@ static int listen_bind(rad_listen_t *this)
if (!fr_sockaddr2ipaddr(&src, sizeof_src,
&sock->my_ipaddr, &sock->my_port)) {
+ close(this->fd);
ERROR("Socket has unsupported address family");
return -1;
}
@@ -3131,9 +3389,9 @@ static int listen_bind(rad_listen_t *this)
#ifdef WITH_TCP
if (sock->proto == IPPROTO_TCP) {
/*
- * Woker threads are blocking.
+ * If we dedicate a worker thread to each socket, then the socket is blocking.
*
- * Otherwise, they're non-blocking.
+ * Otherwise, all input TCP sockets are non-blocking.
*/
if (!this->workers) {
if (fr_nonblock(this->fd) < 0) {
@@ -3331,11 +3589,15 @@ rad_listen_t *proxy_new_listener(TALLOC_CTX *ctx, home_server_t *home, uint16_t
* FIXME: connect() is blocking!
* We do this with the proxy mutex locked, which may
* cause large delays!
- *
- * http://www.developerweb.net/forum/showthread.php?p=13486
*/
this->fd = fr_socket_client_tcp(&home->src_ipaddr,
- &home->ipaddr, home->port, false);
+ &home->ipaddr, home->port,
+#ifdef WITH_TLS
+ !this->nonblock
+#else
+ false
+#endif
+ );
/*
* Set max_requests, lifetime, and idle_timeout from the home server.
@@ -3378,10 +3640,25 @@ rad_listen_t *proxy_new_listener(TALLOC_CTX *ctx, home_server_t *home, uint16_t
this->nonblock |= home->nonblock;
+#ifdef TCP_NODELAY
+ /*
+ * Also set TCP_NODELAY, to force the data to be written quickly.
+ */
+ if (sock->proto == IPPROTO_TCP) {
+ int on = 1;
+
+ if (setsockopt(this->fd, SOL_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) {
+ ERROR("(TLS) Failed to set TCP_NODELAY: %s", fr_syserror(errno));
+ goto error;
+ }
+ }
+#endif
+
/*
* Set non-blocking if it's configured.
*/
if (this->nonblock) {
+ fr_assert(0);
if (fr_nonblock(this->fd) < 0) {
ERROR("(TLS) Failed setting nonblocking for proxy socket '%s' - %s", buffer, fr_strerror());
goto error;
@@ -3394,15 +3671,34 @@ rad_listen_t *proxy_new_listener(TALLOC_CTX *ctx, home_server_t *home, uint16_t
goto error;
}
-#ifdef TCP_NODELAY
+ } else {
/*
- * Also set TCP_NODELAY, to force the data to be written quickly.
+ * Only set timeouts when the socket is blocking. This allows blocking
+ * sockets to still time out when the underlying socket is dead.
*/
- if (sock->proto == IPPROTO_TCP) {
- int on = 1;
+#ifdef SO_RCVTIMEO
+ if (sock->limit.read_timeout) {
+ struct timeval tv;
+
+ tv.tv_sec = sock->limit.read_timeout;
+ tv.tv_usec = 0;
- if (setsockopt(this->fd, SOL_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) {
- ERROR("(TLS) Failed to set TCP_NODELAY: %s", fr_syserror(errno));
+ if (setsockopt(this->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
+ ERROR("(TLS) Failed to set read_timeout: %s", fr_syserror(errno));
+ goto error;
+ }
+ }
+#endif
+
+#ifdef SO_SNDTIMEO
+ if (sock->limit.write_timeout) {
+ struct timeval tv;
+
+ tv.tv_sec = sock->limit.write_timeout;
+ tv.tv_usec = 0;
+
+ if (setsockopt(this->fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) {
+ ERROR("(TLS) Failed to set write_timeout: %s", fr_syserror(errno));
goto error;
}
}
@@ -3427,6 +3723,7 @@ rad_listen_t *proxy_new_listener(TALLOC_CTX *ctx, home_server_t *home, uint16_t
}
#endif
+
sock->connect_timeout = home->connect_timeout;
this->recv = proxy_tls_recv;
@@ -3575,7 +3872,9 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, char const *server)
char const *value;
fr_dlhandle handle;
CONF_SECTION *server_cs;
+#ifdef WITH_TCP
char const *p;
+#endif
char buffer[32];
cp = cf_pair_find(cs, "type");
@@ -3864,7 +4163,9 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head, bool spawn_flag)
if (override) {
cs = cf_section_sub_find_name2(config, "server",
main_config.name);
- if (cs) this->server = main_config.name;
+ if (!cs) cs = cf_section_sub_find_name2(config, "server",
+ "default");
+ if (cs) this->server = cf_section_name2(cs);
}
*last = this;
diff --git a/src/main/mainconfig.c b/src/main/mainconfig.c
index 227ae4a..2b2dda8 100644
--- a/src/main/mainconfig.c
+++ b/src/main/mainconfig.c
@@ -73,6 +73,8 @@ static char const *gid_name = NULL;
static char const *chroot_dir = NULL;
static bool allow_core_dumps = false;
static char const *radlog_dest = NULL;
+static char const *require_message_authenticator = NULL;
+static char const *limit_proxy_state = NULL;
/*
* These are not used anywhere else..
@@ -87,6 +89,56 @@ static bool do_colourise = false;
static char const *radius_dir = NULL; //!< Path to raddb directory
+#ifndef HAVE_KQUEUE
+static uint32_t max_fds = 0;
+#endif
+
+static const FR_NAME_NUMBER fr_bool_auto_names[] = {
+ { "false", FR_BOOL_FALSE },
+ { "no", FR_BOOL_FALSE },
+ { "0", FR_BOOL_FALSE },
+
+ { "true", FR_BOOL_TRUE },
+ { "yes", FR_BOOL_TRUE },
+ { "1", FR_BOOL_TRUE },
+
+ { "auto", FR_BOOL_AUTO },
+
+ { NULL, 0 }
+};
+
+/*
+ * Get decent values for false / true / auto
+ */
+int fr_bool_auto_parse(CONF_PAIR *cp, fr_bool_auto_t *out, char const *str)
+{
+ int value;
+
+ /*
+ * Don't change anything.
+ */
+ if (!str) return 0;
+
+ value = fr_str2int(fr_bool_auto_names, str, -1);
+ if (value >= 0) {
+ *out = value;
+ return 0;
+ }
+
+ /*
+ * This should never happen, as the defaults are in the
+ * source code. If there's no CONF_PAIR, and there's a
+ * parse error, then the source code is wrong.
+ */
+ if (!cp) {
+ fprintf(stderr, "%s: Error - Invalid value in configuration", main_config.name);
+ return -1;
+ }
+
+ cf_log_err(cf_pair_to_item(cp), "Invalid value for \"%s\"", cf_pair_attr(cp));
+ return -1;
+}
+
/**********************************************************************
*
* We need to figure out where the logs go, before doing anything
@@ -160,6 +212,8 @@ static const CONF_PARSER security_config[] = {
{ "max_attributes", FR_CONF_POINTER(PW_TYPE_INTEGER, &fr_max_attributes), STRINGIFY(0) },
{ "reject_delay", FR_CONF_POINTER(PW_TYPE_TIMEVAL, &main_config.reject_delay), STRINGIFY(0) },
{ "status_server", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.status_server), "no"},
+ { "require_message_authenticator", FR_CONF_POINTER(PW_TYPE_STRING, &require_message_authenticator), "auto"},
+ { "limit_proxy_state", FR_CONF_POINTER(PW_TYPE_STRING, &limit_proxy_state), "auto"},
#ifdef ENABLE_OPENSSL_VERSION_CHECK
{ "allow_vulnerable_openssl", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.allow_vulnerable_openssl), "no"},
#endif
@@ -195,8 +249,12 @@ static const CONF_PARSER server_config[] = {
{ "panic_action", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.panic_action), NULL},
{ "hostname_lookups", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &fr_dns_lookups), "no" },
{ "max_request_time", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.max_request_time), STRINGIFY(MAX_REQUEST_TIME) },
+ { "proxy_dedup_window", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.proxy_dedup_window), "1" },
{ "cleanup_delay", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.cleanup_delay), STRINGIFY(CLEANUP_DELAY) },
{ "max_requests", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.max_requests), STRINGIFY(MAX_REQUESTS) },
+#ifndef HAVE_KQUEUE
+ { "max_fds", FR_CONF_POINTER(PW_TYPE_INTEGER, &max_fds), "512" },
+#endif
{ "postauth_client_lost", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.postauth_client_lost), "no" },
{ "pidfile", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.pid_file), "${run_dir}/radiusd.pid"},
{ "checkrad", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.checkrad), "${sbindir}/checkrad" },
@@ -864,6 +922,8 @@ int main_config_init(void)
if (!main_config.dictionary_dir) {
main_config.dictionary_dir = DICTDIR;
}
+ main_config.require_ma = FR_BOOL_AUTO;
+ main_config.limit_proxy_state = FR_BOOL_AUTO;
/*
* About sizeof(REQUEST) + sizeof(RADIUS_PACKET) * 2 + sizeof(VALUE_PAIR) * 400
@@ -1144,6 +1204,10 @@ do {\
if ((main_config.reject_delay.tv_sec != 0) || (main_config.reject_delay.tv_usec != 0)) {
FR_TIMEVAL_BOUND_CHECK("reject_delay", &main_config.reject_delay, >=, 1, 0);
}
+
+ FR_INTEGER_BOUND_CHECK("proxy_dedup_window", main_config.proxy_dedup_window, <=, 10);
+ FR_INTEGER_BOUND_CHECK("proxy_dedup_window", main_config.proxy_dedup_window, >=, 1);
+
FR_TIMEVAL_BOUND_CHECK("reject_delay", &main_config.reject_delay, <=, 10, 0);
FR_INTEGER_BOUND_CHECK("cleanup_delay", main_config.cleanup_delay, <=, 30);
@@ -1159,6 +1223,46 @@ do {\
main_config.init_delay.tv_sec = 0;
main_config.init_delay.tv_usec = 2* (1000000 / 3);
+ {
+ CONF_PAIR *cp = NULL;
+
+ subcs = cf_section_sub_find(cs, "security");
+ if (subcs) cp = cf_pair_find(subcs, "require_message_authenticator");
+ if (fr_bool_auto_parse(cp, &main_config.require_ma, require_message_authenticator) < 0) {
+ cf_file_free(cs);
+ return -1;
+ }
+
+ if (subcs) cp = cf_pair_find(subcs, "limit_proxy_state");
+ if (fr_bool_auto_parse(cp, &main_config.limit_proxy_state, limit_proxy_state) < 0) {
+ cf_file_free(cs);
+ return -1;
+ }
+ }
+
+#ifndef HAVE_KQUEUE
+ /*
+ * select() is limited to 1024 file descriptors. :(
+ */
+ if (max_fds) {
+ if (max_fds > FD_SETSIZE) {
+ fr_ev_max_fds = FD_SETSIZE;
+ } else {
+ /*
+ * Round up to the next highest power of 2.
+ */
+ max_fds--;
+ max_fds |= max_fds >> 1;
+ max_fds |= max_fds >> 2;
+ max_fds |= max_fds >> 4;
+ max_fds |= max_fds >> 8;
+ max_fds |= max_fds >> 16;
+ max_fds++;
+ fr_ev_max_fds = max_fds;
+ }
+ }
+#endif
+
/*
* Free the old configuration items, and replace them
* with the new ones.
diff --git a/src/main/map.c b/src/main/map.c
index e59fcec..34683a2 100644
--- a/src/main/map.c
+++ b/src/main/map.c
@@ -1108,7 +1108,7 @@ int map_to_request(REQUEST *request, vp_map_t const *map, radius_map_getvalue_t
*/
if (((map->lhs->tmpl_list == PAIR_LIST_COA) ||
(map->lhs->tmpl_list == PAIR_LIST_DM)) && !request->coa) {
- if (request->parent) {
+ if (context->parent) {
REDEBUG("You can only do 'update coa' when processing a packet which was received from the network");
return -2;
}
diff --git a/src/main/modcall.c b/src/main/modcall.c
index aa6abf8..5a3116c 100644
--- a/src/main/modcall.c
+++ b/src/main/modcall.c
@@ -311,7 +311,7 @@ static rlm_rcode_t CC_HINT(nonnull) call_modsingle(rlm_components_t component, m
*/
blocked = (request->master_state == REQUEST_STOP_PROCESSING);
if (blocked) {
- RWARN("Module %s became unblocked", sp->modinst->entry->name);
+ RWARN("Module %s(%s) became unblocked", sp->modinst->name, sp->modinst->entry->name);
}
fail:
diff --git a/src/main/modules.c b/src/main/modules.c
index fd4334d..9ccb310 100644
--- a/src/main/modules.c
+++ b/src/main/modules.c
@@ -571,10 +571,10 @@ static int module_conf_parse(module_instance_t *node, void **handle)
* If there is supposed to be instance data, allocate it now.
* Also parse the configuration data, if required.
*/
- if (node->entry->module->inst_size) {
- *handle = talloc_zero_array(node, uint8_t, node->entry->module->inst_size);
- rad_assert(*handle);
+ *handle = talloc_zero_array(node, uint8_t, node->entry->module->inst_size);
+ rad_assert(*handle);
+ if (node->entry->module->inst_size) {
talloc_set_name(*handle, "rlm_%s_t",
node->entry->module->name ? node->entry->module->name : "config");
diff --git a/src/main/process.c b/src/main/process.c
index ed77839..9880e34 100644
--- a/src/main/process.c
+++ b/src/main/process.c
@@ -1006,6 +1006,12 @@ static void request_cleanup_delay_init(REQUEST *request)
#ifdef HAVE_PTHREAD_H
rad_assert(request->child_pid == NO_SUCH_CHILD_PID);
#endif
+
+ /*
+ * Set the statistics immediately if we can.
+ */
+ request_stats_final(request);
+
STATE_MACHINE_TIMER(FR_ACTION_TIMER);
return;
}
@@ -1258,6 +1264,9 @@ static void request_cleanup_delay(REQUEST *request, int action)
#ifdef DEBUG_STATE_MACHINE
if (rad_debug_lvl) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_cleanup_delay");
#endif
+
+ request_stats_final(request);
+
STATE_MACHINE_TIMER(FR_ACTION_TIMER);
return;
} /* else it's time to clean up */
@@ -1486,7 +1495,7 @@ static void request_finish(REQUEST *request, int action)
/*
* Maybe originate a CoA request.
*/
- if ((action == FR_ACTION_RUN) && !request->proxy && request->coa) {
+ if ((action == FR_ACTION_RUN) && (!request->proxy || request->proxy->dst_port == 0) && request->coa) {
request_coa_originate(request);
}
#endif
@@ -1591,9 +1600,14 @@ static void request_finish(REQUEST *request, int action)
#ifdef WITH_PROXY
/*
* If we timed out a proxy packet, don't delay
- * the reject any more.
+ * the reject any more. Or, if we proxied it to
+ * a real home server, then don't delay it.
+ *
+ * We don't want to have each proxy in a chain
+ * adding their own reject delay, which would
+ * result in N*reject_delays being applied.
*/
- if (request->proxy && !request->proxy_reply) {
+ if (request->proxy && (!request->proxy_reply || request->proxy->dst_port != 0)) {
request->response_delay.tv_sec = 0;
request->response_delay.tv_usec = 0;
}
@@ -2024,6 +2038,10 @@ static REQUEST *request_setup(TALLOC_CTX *ctx, rad_listen_t *listener, RADIUS_PA
return NULL;
}
+#ifdef WITH_RADIUSV11
+ request->reply->radiusv11 = packet->radiusv11;
+#endif
+
request->listener = listener;
request->client = client;
request->packet = talloc_steal(request, packet);
@@ -2286,16 +2304,6 @@ static void remove_from_proxy_hash_nl(REQUEST *request, bool yank)
if (!request->in_proxy_hash) return;
-#ifdef COA_TUNNEL
- /*
- * Track how many IDs are used. This information
- * helps the listen_coa_find() function get a
- * listener which has free IDs.
- */
- rad_assert(request->proxy_listener->num_ids_used > 0);
- request->proxy_listener->num_ids_used--;
-#endif
-
fr_packet_list_id_free(proxy_list, request->proxy, yank);
request->in_proxy_hash = false;
@@ -2341,6 +2349,18 @@ static void remove_from_proxy_hash_nl(REQUEST *request, bool yank)
if (request->proxy_listener) {
request->proxy_listener->count--;
+
+#ifdef WITH_COA_TUNNEL
+ /*
+ * Track how many IDs are used. This information
+ * helps the listen_coa_find() function get a
+ * listener which has free IDs.
+ */
+ if (request->proxy_listener->send_coa) {
+ rad_assert(request->proxy_listener->num_ids_used > 0);
+ request->proxy_listener->num_ids_used--;
+ }
+#endif
}
request->proxy_listener = NULL;
@@ -2361,18 +2381,6 @@ static void remove_from_proxy_hash(REQUEST *request)
*/
if (!request->in_proxy_hash) return;
-#ifdef WITH_TCP
- /*
- * Status-Server packets aren't removed from the proxy hash. They're reused.
- *
- * Unless we're tearing down the listener.
- */
- if ((request->proxy->proto == IPPROTO_TCP) && (request->proxy->code == PW_CODE_STATUS_SERVER) &&
- request->proxy_listener && (request->proxy_listener->status < RAD_LISTEN_STATUS_EOL)) {
- return;
- }
-#endif
-
/*
* The "not in hash" flag is definitive. However, if the
* flag says that it IS in the hash, there might still be
@@ -2493,13 +2501,13 @@ static int insert_into_proxy_hash(REQUEST *request)
goto fail;
}
-#ifdef COA_TUNNEL
+#ifdef WITH_COA_TUNNEL
/*
* Track how many IDs are used. This information
* helps the listen_coa_find() function get a
* listener which has free IDs.
*/
- request->proxy_listener->num_ids_used++;
+ if (request->proxy_listener->send_coa) request->proxy_listener->num_ids_used++;
#endif
/*
@@ -2790,16 +2798,78 @@ int request_proxy_reply(RADIUS_PACKET *packet)
* server core, but I guess we can fix that later.
*/
if (!request->proxy_reply) {
+ decode_fail_t reason;
+
+ /*
+ * If the home server configuration requires a Message-Authenticator, then set the flag,
+ * but only if the proxied packet is Access-Request or Status-Sercer.
+ *
+ * The realms.c file already clears require_ma for TLS connections.
+ */
+ bool require_ma = (request->home_server->require_ma == FR_BOOL_TRUE) && (request->proxy->code == PW_CODE_ACCESS_REQUEST);
+
if (!request->home_server) {
proxy_reply_too_late(request);
return 0;
}
+ if (!rad_packet_ok(packet, require_ma, &reason)) {
+ DEBUG("Ignoring invalid packet - %s", fr_strerror());
+ return 0;
+ }
+
if (rad_verify(packet, request->proxy,
request->home_server->secret) != 0) {
DEBUG("Ignoring spoofed proxy reply. Signature is invalid");
return 0;
}
+
+ /*
+ * BlastRADIUS checks. We're running in the main
+ * listener thread, so there's no conflict
+ * checking or setting these fields.
+ */
+ if ((request->proxy->code == PW_CODE_ACCESS_REQUEST) &&
+#ifdef WITH_TLS
+ !request->home_server->tls &&
+#endif
+ !packet->eap_message) {
+ if (request->home_server->require_ma == FR_BOOL_AUTO) {
+ if (!packet->message_authenticator) {
+ RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ RERROR("BlastRADIUS check: Received response to Access-Request without Message-Authenticator.");
+ RERROR("Setting \"require_message_authenticator = false\" for home_server %s", request->home_server->name);
+ RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ RERROR("UPGRADE THE HOME SERVER AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK.");
+ RERROR("Once the home_server is upgraded, set \"require_message_authenticator = true\" for home_server %s.", request->home_server->name);
+ RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+
+ request->home_server->require_ma = FR_BOOL_FALSE;
+ } else {
+ RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ RERROR("BlastRADIUS check: Received response to Access-Request with Message-Authenticator.");
+ RERROR("Setting \"require_message_authenticator = true\" for home_server %s", request->home_server->name);
+ RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ RERROR("It looks like the home server has been updated to protect from the BlastRADIUS attack.");
+ RERROR("Please set \"require_message_authenticator = true\" for home_server %s", request->home_server->name);
+ RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+
+ request->home_server->require_ma = FR_BOOL_TRUE;
+ }
+
+ } else if (fr_debug_lvl && (request->home_server->require_ma == FR_BOOL_FALSE) && !packet->message_authenticator) {
+ /*
+ * If it's "no" AND we don't have a Message-Authenticator, then complain on every packet.
+ */
+ RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ RDEBUG("BlastRADIUS check: Received packet without Message-Authenticator from home_server %s", request->home_server->name);
+ RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ RDEBUG("The packet does not contain Message-Authenticator, which is a security issue");
+ RDEBUG("UPGRADE THE HOME SERVER AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK.");
+ RDEBUG("Once the home server is upgraded, set \"require_message_authenticator = true\" for home_server %s", request->home_server->name);
+ RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
+ }
+ }
}
/*
@@ -2863,6 +2933,18 @@ int request_proxy_reply(RADIUS_PACKET *packet)
#ifdef WITH_STATS
/*
+ * The average includes our time to receive packets and
+ * look them up in the hashes, which should be the same
+ * for all packets.
+ *
+ * We update the response time only for the FIRST packet
+ * we receive.
+ */
+ if (request->home_server->ema.window > 0) {
+ radius_stats_ema(&request->home_server->ema, &request->proxy->timestamp, &now);
+ }
+
+ /*
* Update the proxy listener stats here, because only one
* thread accesses that at a time. The home_server and
* main proxy_*_stats structures are updated once the
@@ -3193,6 +3275,14 @@ static int request_will_proxy(REQUEST *request)
pool = home_pool_byname(vp->vp_strvalue, pool_type);
/*
+ * If we didn't find an auth only or acct only pool
+ * fall-back to those which do both.
+ */
+ if (!pool && ((pool_type == HOME_TYPE_AUTH) || (pool_type == HOME_TYPE_ACCT))) {
+ pool = home_pool_byname(vp->vp_strvalue, HOME_TYPE_AUTH_ACCT);
+ }
+
+ /*
* Send it directly to a home server (i.e. NAS)
*/
} else if (((vp = fr_pair_find_by_num(request->config, PW_PACKET_DST_IP_ADDRESS, 0, TAG_ANY)) != NULL) ||
@@ -3292,6 +3382,15 @@ static int request_will_proxy(REQUEST *request)
* Find the home server by name.
*/
home = home_server_byname(vp->vp_strvalue, type);
+
+ /*
+ * If we didn't find an auth only or acct only home server
+ * fall-back to those which do both.
+ */
+ if (!home && ((type == HOME_TYPE_AUTH) || (type == HOME_TYPE_ACCT))) {
+ home = home_server_byname(vp->vp_strvalue, HOME_TYPE_AUTH_ACCT);
+ }
+
if (!home) {
RWDEBUG("No such home server %s", vp->vp_strvalue);
return 0;
@@ -3361,7 +3460,7 @@ static int request_will_proxy(REQUEST *request)
home = home_server_ldb(realmname, pool, request);
if (!home) {
- REDEBUG2("Failed to find live home server: Cancelling proxy");
+ REDEBUG2("Failed to find live home server for realm %s: Cancelling proxy", realmname);
return -1;
}
@@ -3373,7 +3472,11 @@ do_home:
* Once we've decided to proxy a request, we cannot send
* a CoA packet. So we free up any CoA packet here.
*/
- if (request->coa) request_done(request->coa, FR_ACTION_COA_CANCELLED);
+ if (request->coa) {
+ RWDEBUG("Cannot proxy and originate CoA packets at the same time. Cancelling CoA request");
+ request_done(request->coa, FR_ACTION_COA_CANCELLED);
+ request->coa = NULL;
+ }
#endif
/*
@@ -3605,6 +3708,7 @@ static int request_proxy(REQUEST *request)
if (request->coa) {
RWDEBUG("Cannot proxy and originate CoA packets at the same time. Cancelling CoA request");
request_done(request->coa, FR_ACTION_COA_CANCELLED);
+ request->coa = NULL;
}
#endif
@@ -3817,6 +3921,7 @@ static void request_ping(REQUEST *request, int action)
break;
case FR_ACTION_PROXY_REPLY:
+ default:
rad_assert(request->in_proxy_hash);
request->home_server->num_received_pings++;
@@ -3855,9 +3960,10 @@ static void request_ping(REQUEST *request, int action)
mark_home_server_alive(request, home);
break;
- default:
+ case FR_ACTION_RUN:
+ case FR_ACTION_DUP:
RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]);
- break;
+ return;
}
rad_assert(!request->in_request_hash);
@@ -4339,10 +4445,10 @@ static void proxy_wait_for_reply(REQUEST *request, int action)
* and should be suppressed by the proxy.
*/
when = request->proxy->timestamp;
- when.tv_sec++;
+ when.tv_sec += main_config.proxy_dedup_window;
if (timercmp(&now, &when, <)) {
- DEBUG2("Suppressing duplicate proxied request (too fast) to home server %s port %d proto TCP - ID: %d",
+ DEBUG2("Suppressing duplicate proxied request (too fast) to home server %s port %d - ID: %d",
inet_ntop(request->proxy->dst_ipaddr.af,
&request->proxy->dst_ipaddr.ipaddr,
buffer, sizeof(buffer)),
@@ -4543,9 +4649,9 @@ static void request_coa_originate(REQUEST *request)
VERIFY_REQUEST(request);
rad_assert(request->coa != NULL);
- rad_assert(request->proxy == NULL);
+ rad_assert(request->proxy == NULL || request->proxy->dst_port == 0);
rad_assert(!request->in_proxy_hash);
- rad_assert(request->proxy_reply == NULL);
+ rad_assert(request->proxy_reply == NULL || request->proxy_reply->src_port == 0);
/*
* Check whether we want to originate one, or cancel one.
@@ -5018,7 +5124,11 @@ static bool coa_max_time(REQUEST *request)
buffer, sizeof(buffer)),
request->proxy->dst_port,
mrd);
- request_done(request, FR_ACTION_DONE);
+ if (setup_post_proxy_fail(request)) {
+ request_queue_or_run(request, coa_no_reply);
+ } else {
+ request_done(request, FR_ACTION_DONE);
+ }
return true;
}
@@ -5386,7 +5496,6 @@ static void listener_free_cb(void *ctx)
talloc_free(this);
}
-#ifdef WITH_TCP
#ifdef WITH_PROXY
static int proxy_eol_cb(void *ctx, void *data)
{
@@ -5426,7 +5535,6 @@ static int proxy_eol_cb(void *ctx, void *data)
return 0;
}
#endif /* WITH_PROXY */
-#endif /* WITH_TCP */
static void event_new_fd(rad_listen_t *this)
{
@@ -5602,6 +5710,7 @@ static void event_new_fd(rad_listen_t *this)
*/
this->print(this, buffer, sizeof(buffer));
ERROR("Failed adding event handler for socket %s: %s", buffer, fr_strerror());
+
this->status = RAD_LISTEN_STATUS_EOL;
goto listener_is_eol;
} /* end of INIT */
@@ -5645,6 +5754,7 @@ static void event_new_fd(rad_listen_t *this)
fr_event_fd_delete(el, 0, this->fd);
this->status = RAD_LISTEN_STATUS_REMOVE_NOW;
}
+#endif /* WITH_TCP */
/*
* The socket has had a catastrophic error. Close it.
@@ -5708,7 +5818,6 @@ static void event_new_fd(rad_listen_t *this)
*/
this->status = RAD_LISTEN_STATUS_REMOVE_NOW;
} /* socket is at EOL */
-#endif /* WITH_TCP */
if (this->dead) goto wait_some_more;
@@ -6147,7 +6256,7 @@ static void check_proxy(rad_listen_t *head)
if (sock->my_ipaddr.af == AF_INET) has_v4 = true;
if (sock->my_ipaddr.af == AF_INET6) has_v6 = true;
break;
-
+
default:
break;
}
diff --git a/src/main/radclient.c b/src/main/radclient.c
index 49da461..ab880dd 100644
--- a/src/main/radclient.c
+++ b/src/main/radclient.c
@@ -60,11 +60,13 @@ static fr_ipaddr_t server_ipaddr;
static int resend_count = 1;
static bool done = true;
static bool print_filename = false;
+static bool blast_radius = false;
static fr_ipaddr_t client_ipaddr;
static uint16_t client_port = 0;
static int sockfd;
+static int last_used_id = -1;
#ifdef WITH_TCP
static char const *proto = NULL;
@@ -95,6 +97,7 @@ static void NEVER_RETURNS usage(void)
fprintf(stderr, " <command> One of auth, acct, status, coa, disconnect or auto.\n");
fprintf(stderr, " -4 Use IPv4 address of server\n");
fprintf(stderr, " -6 Use IPv6 address of server.\n");
+ fprintf(stderr, " -b Mandate checks for Blast RADIUS issue (this is not set by default).\n");
fprintf(stderr, " -c <count> Send each packet 'count' times.\n");
fprintf(stderr, " -d <raddb> Set user dictionary directory (defaults to " RADDBDIR ").\n");
fprintf(stderr, " -D <dictdir> Set main dictionary directory (defaults to " DICTDIR ").\n");
@@ -416,7 +419,7 @@ static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files)
#endif
request->files = files;
- request->packet->id = -1; /* allocate when sending */
+ request->packet->id = last_used_id; /* either requested, or allocated by the library */
request->num = num++;
/*
@@ -892,7 +895,7 @@ static int send_one_packet(rc_request_t *request)
/*
* Haven't sent the packet yet. Initialize it.
*/
- if (request->packet->id == -1) {
+ if (!request->tries || (request->packet->id == -1)) {
int i;
bool rcode;
@@ -949,8 +952,18 @@ static int send_one_packet(rc_request_t *request)
assert(request->packet->id != -1);
assert(request->packet->data == NULL);
- for (i = 0; i < 4; i++) {
- ((uint32_t *) request->packet->vector)[i] = fr_rand();
+ if (request->packet->code == PW_CODE_ACCESS_REQUEST) {
+ VALUE_PAIR *vp;
+
+ if (((vp = fr_pair_find_by_num(request->packet->vps, PW_PACKET_AUTHENTICATION_VECTOR, 0, TAG_ANY)) != NULL) &&
+ (vp->vp_length >= 16)) {
+ memcpy(request->packet->vector, vp->vp_octets, 16);
+
+ } else {
+ for (i = 0; i < 4; i++) {
+ ((uint32_t *) request->packet->vector)[i] = fr_rand();
+ }
+ }
}
/*
@@ -1048,11 +1061,15 @@ static int send_one_packet(rc_request_t *request)
REDEBUG("Failed to send packet for ID %d", request->packet->id);
deallocate_id(request);
request->done = true;
+ stats.lost++;
return -1;
}
if (fr_log_fp) {
fr_packet_header_print(fr_log_fp, request->packet, false);
+
+ if (fr_debug_lvl > 2) rad_print_hex(request->packet);
+
if (fr_debug_lvl > 0) vp_printlist(fr_log_fp, request->packet->vps);
}
@@ -1060,6 +1077,130 @@ static int send_one_packet(rc_request_t *request)
}
/*
+ * Do Blast RADIUS checks.
+ *
+ * The request is an Access-Request, and does NOT contain Proxy-State.
+ *
+ * The reply is a raw packet, and is NOT yet decoded.
+ */
+static int blast_radius_check(rc_request_t *request, RADIUS_PACKET *reply)
+{
+ uint8_t *attr, *end;
+ VALUE_PAIR *vp;
+ bool have_message_authenticator = false;
+
+ /*
+ * We've received a raw packet. Nothing has (as of yet) checked
+ * anything in it other than the length, and that it's a
+ * well-formed RADIUS packet.
+ */
+ switch (reply->data[0]) {
+ case PW_CODE_ACCESS_ACCEPT:
+ case PW_CODE_ACCESS_REJECT:
+ case PW_CODE_ACCESS_CHALLENGE:
+ if (reply->data[1] != request->packet->id) {
+ ERROR("Invalid reply ID %d to Access-Request ID %d", reply->data[1], request->packet->id);
+ return -1;
+ }
+ break;
+
+ default:
+ ERROR("Invalid reply code %d to Access-Request", reply->data[0]);
+ return -1;
+ }
+
+ /*
+ * If the reply has a Message-Authenticator, then it MIGHT be fine.
+ */
+ attr = reply->data + 20;
+ end = reply->data + reply->data_len;
+
+ /*
+ * It should be the first attribute, so we warn if it isn't there.
+ *
+ * But it's not a fatal error.
+ */
+ if (blast_radius && (attr[0] != PW_MESSAGE_AUTHENTICATOR)) {
+ RDEBUG("WARNING The %s reply packet does not have Message-Authenticator as the first attribute. The packet may be vulnerable to Blast RADIUS attacks.",
+ fr_packet_codes[reply->data[0]]);
+ }
+
+ /*
+ * Set up for Proxy-State checks.
+ *
+ * If we see a Proxy-State in the reply which we didn't send, then it's a Blast RADIUS attack.
+ */
+ vp = fr_pair_find_by_num(request->packet->vps, PW_PROXY_STATE, 0, TAG_ANY);
+
+ while (attr < end) {
+ /*
+ * Blast RADIUS work-arounds require that
+ * Message-Authenticator is the first attribute in the
+ * reply. Note that we don't check for it being the
+ * first attribute, but simply that it exists.
+ *
+ * That check is a balance between securing the reply
+ * packet from attacks, and not violating the RFCs which
+ * say that there is no order to attributes in the
+ * packet.
+ *
+ * However, no matter the status of the '-b' flag we
+ * still can check for the signature of the attack, and
+ * discard packets which are suspicious. This behavior
+ * protects radclient from the attack, without mandating
+ * new behavior on the server side.
+ *
+ * Note that we don't set the '-b' flag by default.
+ * radclient is intended for testing / debugging, and is
+ * not intended to be used as part of a secure login /
+ * user checking system.
+ */
+ if (attr[0] == PW_MESSAGE_AUTHENTICATOR) {
+ have_message_authenticator = true;
+ goto next;
+ }
+
+ /*
+ * If there are Proxy-State attributes in the reply, they must
+ * match EXACTLY the Proxy-State attributes in the request.
+ *
+ * Note that we don't care if there are more Proxy-States
+ * in the request than in the reply. The Blast RADIUS
+ * issue requires _adding_ Proxy-State attributes, and
+ * cannot work when the server _deletes_ Proxy-State
+ * attributes.
+ */
+ if (attr[0] == PW_PROXY_STATE) {
+ if (!vp || (vp->length != (size_t) (attr[1] - 2)) || (memcmp(vp->vp_octets, attr + 2, vp->length) != 0)) {
+ ERROR("Invalid reply to Access-Request ID %d - Discarding packet due to Blast RADIUS attack being detected.", request->packet->id);
+ ERROR("We received a Proxy-State in the reply which we did not send, or which is different from what we sent.");
+ return -1;
+ }
+
+ vp = fr_pair_find_by_num(vp->next, PW_PROXY_STATE, 0, TAG_ANY);
+ }
+
+ next:
+ attr += attr[1];
+ }
+
+ /*
+ * If "-b" is set, then we require Message-Authenticator in the reply.
+ */
+ if (blast_radius && !have_message_authenticator) {
+ ERROR("The %s reply packet does not contain Message-Authenticator - discarding packet due to Blast RADIUS checks.",
+ fr_packet_codes[reply->data[0]]);
+ return -1;
+ }
+
+ /*
+ * The packet doesn't look like it's a Blast RADIUS attack. The
+ * caller will now verify the packet signature.
+ */
+ return 0;
+}
+
+/*
* Receive one packet, maybe.
*/
static int recv_one_packet(int wait_time)
@@ -1101,6 +1242,8 @@ static int recv_one_packet(int wait_time)
return -1; /* bad packet */
}
+ if (fr_debug_lvl > 2) rad_print_hex(reply);
+
packet_p = fr_packet_list_find_byreply(pl, reply);
if (!packet_p) {
ERROR("Received reply to request we did not send. (id=%d socket %d)",
@@ -1111,6 +1254,20 @@ static int recv_one_packet(int wait_time)
request = fr_packet2myptr(rc_request_t, packet, packet_p);
/*
+ * We want radclient to be able to send any packet, including
+ * imperfect ones. However, we do NOT want to be vulnerable to
+ * the "Blast RADIUS" issue. Instead of adding command-line
+ * flags to enable/disable similar flags to what the server
+ * sends, we just do a few more smart checks to double-check
+ * things.
+ */
+ if ((request->packet->code == PW_CODE_ACCESS_REQUEST) &&
+ blast_radius_check(request, reply) < 0) {
+ rad_free(&reply);
+ return -1;
+ }
+
+ /*
* Fails the signature validation: not a real reply.
* FIXME: Silently drop it and listen for another packet.
*/
@@ -1243,7 +1400,7 @@ int main(int argc, char **argv)
exit(1);
}
- while ((c = getopt(argc, argv, "46c:d:D:f:Fhn:p:qr:sS:t:vx"
+ while ((c = getopt(argc, argv, "46bc:d:D:f:Fhi:n:p:qr:sS:t:vx"
#ifdef WITH_TCP
"P:"
#endif
@@ -1256,6 +1413,10 @@ int main(int argc, char **argv)
force_af = AF_INET6;
break;
+ case 'b':
+ blast_radius = true;
+ break;
+
case 'c':
if (!isdigit((uint8_t) *optarg)) usage();
@@ -1297,6 +1458,15 @@ int main(int argc, char **argv)
print_filename = true;
break;
+ case 'i':
+ if (!isdigit((uint8_t) *optarg))
+ usage();
+ last_used_id = atoi(optarg);
+ if ((last_used_id < 0) || (last_used_id > 255)) {
+ usage();
+ }
+ break;
+
case 'n':
persec = atoi(optarg);
if (persec <= 0) usage();
@@ -1562,6 +1732,7 @@ int main(int argc, char **argv)
int n = parallel;
rc_request_t *next;
char const *filename = NULL;
+ time_t wake = 0;
done = true;
sleep_time = -1;
@@ -1569,6 +1740,15 @@ int main(int argc, char **argv)
/*
* Walk over the packets, sending them.
*/
+ for (this = request_head; this != NULL; this = this->next) {
+ if (this->reply) continue;
+
+ if (!this->timestamp) continue;
+
+ if (!wake || (wake > (this->timestamp + ((int) timeout) * (retries - this->tries)))) {
+ wake = this->timestamp + ((int) timeout) * (retries - this->tries);
+ }
+ }
for (this = request_head; this != NULL; this = next) {
next = this->next;
@@ -1613,6 +1793,10 @@ int main(int argc, char **argv)
break;
}
+ if (!wake || (wake > (this->timestamp + ((int) timeout) * (retries - this->tries)))) {
+ wake = this->timestamp + ((int) timeout) * (retries - this->tries);
+ }
+
/*
* Wait a little before sending
* the next packet, if told to.
@@ -1664,7 +1848,18 @@ int main(int argc, char **argv)
* Still have outstanding requests.
*/
if (fr_packet_list_num_elements(pl) > 0) {
+ time_t now = time(NULL);
done = false;
+
+ /*
+ * The last time we wake up for a packet.
+ *
+ * If we're past that time, then give up.
+ */
+ if (wake < now) {
+ break;
+ }
+
} else {
sleep_time = 0;
}
diff --git a/src/main/radsecret b/src/main/radsecret
new file mode 100755
index 0000000..2a03a2e
--- /dev/null
+++ b/src/main/radsecret
@@ -0,0 +1,7 @@
+#!/usr/bin/env perl
+#
+# A tool which generates strong shared secrets.
+#
+use Convert::Base32;
+use Crypt::URandom();
+print join('-', unpack("(A4)*", lc encode_base32(Crypt::URandom::urandom(12)))), "\n";
diff --git a/src/main/radsecret.mk b/src/main/radsecret.mk
new file mode 100644
index 0000000..c5f43b4
--- /dev/null
+++ b/src/main/radsecret.mk
@@ -0,0 +1,5 @@
+install: $(R)/$(bindir)/radsecret
+
+$(R)/$(bindir)/radsecret: ${top_srcdir}/src/main/radsecret
+ @$(ECHO) INSTALL radsecret
+ $(Q)${PROGRAM_INSTALL} -c -m 755 $< $@
diff --git a/src/main/radsniff.c b/src/main/radsniff.c
index e0d2b65..0458d77 100644
--- a/src/main/radsniff.c
+++ b/src/main/radsniff.c
@@ -1804,6 +1804,15 @@ static void _unmark_link(void *request)
this->in_link_tree = false;
}
+/** Exit the event loop after a given timeout.
+ *
+ */
+static void timeout_event(UNUSED void *ctx)
+{
+ fr_event_loop_exit(events, 1);
+}
+
+
#ifdef HAVE_COLLECTDC_H
/** Re-open the collectd socket
*
@@ -1919,6 +1928,7 @@ static void NEVER_RETURNS usage(int status)
fprintf(output, " -R <filter> RADIUS attribute response filter.\n");
fprintf(output, " -s <secret> RADIUS secret.\n");
fprintf(output, " -S Write PCAP data to stdout.\n");
+ fprintf(output, " -t <timeout> Stop after <timeout> seconds.\n");
fprintf(output, " -v Show program version information.\n");
fprintf(output, " -w <file> Write output packets to file.\n");
fprintf(output, " -x Print more debugging information.\n");
@@ -1947,6 +1957,8 @@ int main(int argc, char *argv[])
char buffer[1024];
int opt;
+ unsigned int timeout = 0;
+ fr_event_t *timeout_ev = NULL;
char const *radius_dir = RADDBDIR;
char const *dict_dir = DICTDIR;
@@ -1999,7 +2011,7 @@ int main(int argc, char *argv[])
/*
* Get options
*/
- while ((opt = getopt(argc, argv, "ab:c:Cd:D:e:Ff:hi:I:l:L:mp:P:qr:R:s:Svw:xXW:T:P:N:O:")) != EOF) {
+ while ((opt = getopt(argc, argv, "ab:c:Cd:D:e:Ff:hi:I:l:L:mp:P:qr:R:s:St:vw:xXW:T:P:N:O:")) != EOF) {
switch (opt) {
case 'a':
{
@@ -2120,6 +2132,10 @@ int main(int argc, char *argv[])
conf->radius_secret = optarg;
break;
+ case 't':
+ timeout = atoi(optarg);
+ break;
+
case 'S':
conf->to_stdout = true;
break;
@@ -2569,7 +2585,7 @@ int main(int argc, char *argv[])
* Setup and enter the main event loop. Who needs libev when you can roll your own...
*/
{
- struct timeval now;
+ struct timeval now, when;
rs_update_t update;
char *buff;
@@ -2632,17 +2648,26 @@ int main(int argc, char *argv[])
update.stats = &stats;
update.in = in;
- now.tv_sec += conf->stats.interval;
- now.tv_usec = 0;
- if (!fr_event_insert(events, rs_stats_process, (void *) &update, &now, &event)) {
+ when = now;
+ when.tv_sec += conf->stats.interval;
+ when.tv_usec = 0;
+ if (!fr_event_insert(events, rs_stats_process, (void *) &update, &when, &event)) {
ERROR("Failed inserting stats event");
}
INFO("Muting stats for the next %i milliseconds (warmup)", conf->stats.timeout);
- rs_tv_add_ms(&now, conf->stats.timeout, &stats.quiet);
+ rs_tv_add_ms(&when, conf->stats.timeout, &stats.quiet);
}
- }
+ if (timeout) {
+ when = now;
+ when.tv_sec += timeout;
+
+ if (!fr_event_insert(events, timeout_event, NULL, &when, &timeout_ev)) {
+ ERROR("Failed inserting timeout event");
+ }
+ }
+ }
/*
* Do this as late as possible so we can return an error code if something went wrong.
diff --git a/src/main/realms.c b/src/main/realms.c
index 2959d82..fa42813 100644
--- a/src/main/realms.c
+++ b/src/main/realms.c
@@ -452,6 +452,12 @@ static CONF_PARSER limit_config[] = {
{ "max_requests", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, limit.max_requests), "0" },
{ "lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, limit.lifetime), "0" },
{ "idle_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, limit.idle_timeout), "0" },
+#ifdef SO_RCVTIMEO
+ { "read_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, limit.read_timeout), NULL },
+#endif
+#ifdef SO_SNDTIMEO
+ { "write_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, limit.write_timeout), NULL },
+#endif
CONF_PARSER_TERMINATOR
};
@@ -475,8 +481,11 @@ static CONF_PARSER home_server_recv_coa[] = {
#endif
+static const char *require_message_authenticator = NULL;
+
static CONF_PARSER home_server_config[] = {
{ "nonblock", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, home_server_t, nonblock), "no" },
+ { "require_message_authenticator", FR_CONF_POINTER(PW_TYPE_STRING| PW_TYPE_IGNORE_DEFAULT, &require_message_authenticator), NULL },
{ "ipaddr", FR_CONF_OFFSET(PW_TYPE_COMBO_IP_ADDR, home_server_t, ipaddr), NULL },
{ "ipv4addr", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, home_server_t, ipaddr), NULL },
{ "ipv6addr", FR_CONF_OFFSET(PW_TYPE_IPV6_ADDR, home_server_t, ipaddr), NULL },
@@ -780,6 +789,9 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE
home->cs = cs;
home->state = HOME_STATE_UNKNOWN;
home->proto = IPPROTO_UDP;
+ home->require_ma = main_config.require_ma;
+
+ require_message_authenticator = false;
/*
* Parse the configuration into the home server
@@ -787,6 +799,10 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE
*/
if (cf_section_parse(cs, home, home_server_config) < 0) goto error;
+ if (fr_bool_auto_parse(cf_pair_find(cs, "require_message_authenticator"), &home->require_ma, require_message_authenticator) < 0) {
+ goto error;
+ }
+
/*
* It has an IP address, it must be a remote server.
*/
@@ -1116,11 +1132,18 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE
if (tls) {
int rcode;
+ /*
+ * We don't require this for TLS connections.
+ */
+ home->require_ma = false;
+
home->tls = tls_client_conf_parse(tls);
if (!home->tls) {
goto error;
}
+ home->tls->name = "RADIUS/TLS";
+
/*
* Connection timeouts for outgoing TLS connections.
*/
@@ -3181,7 +3204,7 @@ int home_server_afrom_file(char const *filename)
goto error;
}
-#ifdef COA_TUNNEL
+#ifdef WITH_COA_TUNNEL
if (home->recv_coa) {
fr_strerror_printf("Dynamic home_server '%s' cannot receive CoA requests'", p);
talloc_free(home);
diff --git a/src/main/state.c b/src/main/state.c
index 3700062..ab7a180 100644
--- a/src/main/state.c
+++ b/src/main/state.c
@@ -33,13 +33,14 @@ RCSID("$Id$")
#include <freeradius-devel/process.h>
typedef struct state_entry_t {
- uint8_t state[AUTH_VECTOR_LEN];
+ uint8_t state[MD5_DIGEST_LENGTH];
time_t cleanup;
struct state_entry_t *prev;
struct state_entry_t *next;
int tries;
+ bool ours;
TALLOC_CTX *ctx;
VALUE_PAIR *vps;
@@ -379,10 +380,55 @@ static void fr_state_cleanup(state_entry_t *head)
request_inject(request);
}
+ if (entry->opaque) {
+ entry->free_opaque(entry->opaque);
+ }
+
+ if (entry->ctx) talloc_free(entry->ctx);
+
talloc_free(entry);
}
}
+static void state_entry_calc(REQUEST *request, state_entry_t *entry, VALUE_PAIR *vp)
+{
+ /*
+ * Assume our own State first. This is where the state
+ * is the correct size, AND we're not proxying it to an
+ * external home server. If we are proxying it to an
+ * external home server, then that home server creates
+ * the State attribute, and we don't control it.
+ */
+ if (entry->ours ||
+ (vp->vp_length == sizeof(entry->state) &&
+ (!request->proxy || (request->proxy->dst_port == 0)))) {
+ memcpy(entry->state, vp->vp_octets, sizeof(entry->state));
+ entry->ours = true;
+
+ } else {
+ FR_MD5_CTX ctx;
+
+ /*
+ * We don't control the external State attribute.
+ * As a result, different home servers _may_
+ * create the same State attribute. In order to
+ * differentiate them, we "mix in" the User-Name,
+ * which should contain the realm. And we then
+ * hope that different home servers in the same
+ * realm don't create overlapping State
+ * attributes.
+ */
+ fr_md5_init(&ctx);
+ fr_md5_update(&ctx, vp->vp_octets, vp->vp_length);
+
+ vp = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY);
+ if (vp) fr_md5_update(&ctx, vp->vp_octets, vp->vp_length);
+
+ fr_md5_final(entry->state, &ctx);
+ fr_md5_destroy(&ctx);
+ }
+}
+
/*
* Create a new entry. Called with the mutex held.
@@ -430,17 +476,18 @@ static state_entry_t *fr_state_entry_create(fr_state_t *state, REQUEST *request,
*/
if (old) {
entry->tries = old->tries + 1;
+ entry->ours = old->ours;
/*
* Track State
*/
- if (!vp) {
+ if (!vp && entry->ours) {
memcpy(entry->state, old->state, sizeof(entry->state));
entry->state[1] = entry->state[0] ^ entry->tries;
- entry->state[8] = entry->state[2] ^ ((((uint32_t) HEXIFY(RADIUSD_VERSION)) >> 16) & 0xff);
+ entry->state[8] = entry->state[2] ^ (((uint32_t) HEXIFY(RADIUSD_VERSION)) & 0xff);
entry->state[10] = entry->state[2] ^ ((((uint32_t) HEXIFY(RADIUSD_VERSION)) >> 8) & 0xff);
- entry->state[12] = entry->state[2] ^ (((uint32_t) HEXIFY(RADIUSD_VERSION)) & 0xff);
+ entry->state[12] = entry->state[2] ^ ((((uint32_t) HEXIFY(RADIUSD_VERSION)) >> 16) & 0xff);
}
/*
@@ -457,6 +504,8 @@ static state_entry_t *fr_state_entry_create(fr_state_t *state, REQUEST *request,
x = fr_rand();
memcpy(entry->state + (i * 4), &x, sizeof(x));
}
+
+ entry->ours = true; /* we created it */
}
/*
@@ -464,27 +513,8 @@ static state_entry_t *fr_state_entry_create(fr_state_t *state, REQUEST *request,
* one we created above.
*/
if (vp) {
- /*
- * Assume our own State first.
- */
- if (vp->vp_length == sizeof(entry->state)) {
- memcpy(entry->state, vp->vp_octets, sizeof(entry->state));
+ state_entry_calc(request, entry, vp);
- /*
- * Too big? Get the MD5 hash, in order
- * to depend on the entire contents of State.
- */
- } else if (vp->vp_length > sizeof(entry->state)) {
- fr_md5_calc(entry->state, vp->vp_octets, vp->vp_length);
-
- /*
- * Too small? Use the whole thing, and
- * set the rest of entry->state to zero.
- */
- } else {
- memcpy(entry->state, vp->vp_octets, vp->vp_length);
- memset(&entry->state[vp->vp_length], 0, sizeof(entry->state) - vp->vp_length);
- }
} else {
vp = fr_pair_afrom_num(packet, PW_STATE, 0);
fr_pair_value_memcpy(vp, entry->state, sizeof(entry->state));
@@ -497,7 +527,7 @@ static state_entry_t *fr_state_entry_create(fr_state_t *state, REQUEST *request,
/*
* Make unique for different virtual servers handling same request
*/
- *((uint32_t *)(&entry->state[4])) ^= fr_hash_string(request->server);
+ if (entry->ours) *((uint32_t *)(&entry->state[4])) ^= fr_hash_string(request->server);
/*
* Copy server to state in case it's needed for cleanup
@@ -520,7 +550,7 @@ static state_entry_t *fr_state_entry_create(fr_state_t *state, REQUEST *request,
/*
* Find the entry, based on the State attribute.
*/
-static state_entry_t *fr_state_find(fr_state_t *state, const char *server, RADIUS_PACKET *packet)
+static state_entry_t *fr_state_find(REQUEST *request, fr_state_t *state, const char *server, RADIUS_PACKET *packet)
{
VALUE_PAIR *vp;
state_entry_t *entry, my_entry;
@@ -528,31 +558,12 @@ static state_entry_t *fr_state_find(fr_state_t *state, const char *server, RADIU
vp = fr_pair_find_by_num(packet->vps, PW_STATE, 0, TAG_ANY);
if (!vp) return NULL;
- /*
- * Assume our own State first.
- */
- if (vp->vp_length == sizeof(my_entry.state)) {
- memcpy(my_entry.state, vp->vp_octets, sizeof(my_entry.state));
-
- /*
- * Too big? Get the MD5 hash, in order
- * to depend on the entire contents of State.
- */
- } else if (vp->vp_length > sizeof(my_entry.state)) {
- fr_md5_calc(my_entry.state, vp->vp_octets, vp->vp_length);
-
- /*
- * Too small? Use the whole thing, and
- * set the rest of my_entry.state to zero.
- */
- } else {
- memcpy(my_entry.state, vp->vp_octets, vp->vp_length);
- memset(&my_entry.state[vp->vp_length], 0, sizeof(my_entry.state) - vp->vp_length);
- }
+ my_entry.ours = false;
+ state_entry_calc(request, &my_entry, vp);
/* Make unique for different virtual servers handling same request
*/
- if (server) *((uint32_t *)(&my_entry.state[4])) ^= fr_hash_string(server);
+ if (server && my_entry.ours) *((uint32_t *)(&my_entry.state[4])) ^= fr_hash_string(server);
entry = rbtree_finddata(state->tree, &my_entry);
@@ -576,7 +587,7 @@ void fr_state_discard(REQUEST *request, RADIUS_PACKET *original)
request->state = NULL;
PTHREAD_MUTEX_LOCK(&state->mutex);
- entry = fr_state_find(state, request->server, original);
+ entry = fr_state_find(request, state, request->server, original);
if (entry) state_entry_free(state, entry);
PTHREAD_MUTEX_UNLOCK(&state->mutex);
}
@@ -601,7 +612,7 @@ void fr_state_get_vps(REQUEST *request, RADIUS_PACKET *packet)
rad_assert(request->state == NULL);
PTHREAD_MUTEX_LOCK(&state->mutex);
- entry = fr_state_find(state, request->server, packet);
+ entry = fr_state_find(request, state, request->server, packet);
/*
* This has to be done in a mutex lock, because talloc
@@ -683,7 +694,7 @@ bool fr_state_put_vps(REQUEST *request, RADIUS_PACKET *original, RADIUS_PACKET *
cleanup_list = fr_state_cleanup_find(state);
if (original) {
- old = fr_state_find(state, request->server, original);
+ old = fr_state_find(request, state, request->server, original);
} else {
old = NULL;
}
diff --git a/src/main/stats.c b/src/main/stats.c
index a5c672e..29f2c48 100644
--- a/src/main/stats.c
+++ b/src/main/stats.c
@@ -91,51 +91,57 @@ static void stats_time(fr_stats_t *stats, struct timeval *start,
void request_stats_final(REQUEST *request)
{
rad_listen_t *listener;
+ RADCLIENT *client;
- if (request->master_state == REQUEST_COUNTED) return;
+ if ((request->options & RAD_REQUEST_OPTION_STATS) != 0) return;
- if (!request->listener) return;
- if (!request->client) return;
+ /* don't count statistic requests */
+ if (request->packet->code == PW_CODE_STATUS_SERVER) {
+ return;
+ }
- if ((request->listener->type != RAD_LISTEN_NONE) &&
+ listener = request->listener;
+ if (listener) switch (listener->type) {
+ case RAD_LISTEN_NONE:
#ifdef WITH_ACCOUNTING
- (request->listener->type != RAD_LISTEN_ACCT) &&
+ case RAD_LISTEN_ACCT:
#endif
#ifdef WITH_COA
- (request->listener->type != RAD_LISTEN_COA) &&
+ case RAD_LISTEN_COA:
#endif
- (request->listener->type != RAD_LISTEN_AUTH)) return;
+ case RAD_LISTEN_AUTH:
+ break;
- /* don't count statistic requests */
- if (request->packet->code == PW_CODE_STATUS_SERVER)
- return;
+ default:
+ return;
+ }
/*
* Deal with TCP / TLS issues. The statistics are kept in the parent socket.
*/
- listener = request->listener;
- if (listener->parent) listener = listener->parent;
+ if (listener && listener->parent) listener = listener->parent;
+ client = request->client;
#undef INC_AUTH
-#define INC_AUTH(_x) radius_auth_stats._x++;listener->stats._x++;request->client->auth._x++;
+#define INC_AUTH(_x) radius_auth_stats._x++;if (listener) listener->stats._x++;if (client) client->auth._x++;
#undef INC_ACCT
#ifdef WITH_ACCOUNTING
-#define INC_ACCT(_x) radius_acct_stats._x++;listener->stats._x++;request->client->acct._x++
+#define INC_ACCT(_x) radius_acct_stats._x++;if (listener) listener->stats._x++;if (client) client->acct._x++
#else
#define INC_ACCT(_x)
#endif
#undef INC_COA
#ifdef WITH_COA
-#define INC_COA(_x) radius_coa_stats._x++;listener->stats._x++;request->client->coa._x++
+#define INC_COA(_x) radius_coa_stats._x++;if (listener) listener->stats._x++;if (client) client->coa._x++
#else
#define INC_COA(_x)
#endif
#undef INC_DSC
#ifdef WITH_DSC
-#define INC_DSC(_x) radius_dsc_stats._x++;listener->stats._x++;request->client->dsc._x++
+#define INC_DSC(_x) radius_dsc_stats._x++;if (listener) listener->stats._x++;if (client) client->dsc._x++
#else
#define INC_DSC(_x)
#endif
@@ -148,7 +154,7 @@ void request_stats_final(REQUEST *request)
* deleted, because only the main server thread calls
* this function, which makes it thread-safe.
*/
- if (request->reply && (request->packet->code != PW_CODE_STATUS_SERVER)) switch (request->reply->code) {
+ if (request->reply) switch (request->reply->code) {
case PW_CODE_ACCESS_ACCEPT:
INC_AUTH(total_access_accepts);
@@ -247,25 +253,21 @@ void request_stats_final(REQUEST *request)
switch (request->proxy->code) {
case PW_CODE_ACCESS_REQUEST:
proxy_auth_stats.total_requests += request->num_proxied_requests;
- request->home_server->stats.total_requests += request->num_proxied_requests;
break;
#ifdef WITH_ACCOUNTING
case PW_CODE_ACCOUNTING_REQUEST:
proxy_acct_stats.total_requests += request->num_proxied_requests;
- request->home_server->stats.total_requests += request->num_proxied_requests;
break;
#endif
#ifdef WITH_COA
case PW_CODE_COA_REQUEST:
proxy_coa_stats.total_requests += request->num_proxied_requests;
- request->home_server->stats.total_requests += request->num_proxied_requests;
break;
case PW_CODE_DISCONNECT_REQUEST:
proxy_dsc_stats.total_requests += request->num_proxied_requests;
- request->home_server->stats.total_requests += request->num_proxied_requests;
break;
#endif
@@ -276,7 +278,7 @@ void request_stats_final(REQUEST *request)
if (!request->proxy_reply) goto done; /* simplifies formatting */
#undef INC
-#define INC(_x) proxy_auth_stats._x += request->num_proxied_responses; request->home_server->stats._x += request->num_proxied_responses;
+#define INC(_x) proxy_auth_stats._x += request->num_proxied_responses;request->home_server->stats._x += request->num_proxied_responses;
switch (request->proxy_reply->code) {
case PW_CODE_ACCESS_ACCEPT:
@@ -347,10 +349,7 @@ void request_stats_final(REQUEST *request)
done:
#endif /* WITH_PROXY */
-
if (request->max_time) {
- RADCLIENT *client = request->client;
-
switch (request->packet->code) {
case PW_CODE_ACCESS_REQUEST:
FR_STATS_INC(auth, unresponsive_child);
@@ -376,7 +375,7 @@ void request_stats_final(REQUEST *request)
}
}
- request->master_state = REQUEST_COUNTED;
+ request->options |= RAD_REQUEST_OPTION_STATS;
}
typedef struct fr_stats2vp {
@@ -525,8 +524,8 @@ void request_stats_reply(REQUEST *request)
/*
* Authentication.
*/
- if (((flag->vp_integer & 0x01) != 0) &&
- ((flag->vp_integer & 0xc0) == 0)) {
+ if (((flag->vp_integer & 0x01) != 0) && /* auth */
+ ((flag->vp_integer & 0xe0) == 0)) { /* not client, server or home-server */
request_stats_addvp(request, authvp, &radius_auth_stats);
}
@@ -534,8 +533,8 @@ void request_stats_reply(REQUEST *request)
/*
* Accounting
*/
- if (((flag->vp_integer & 0x02) != 0) &&
- ((flag->vp_integer & 0xc0) == 0)) {
+ if (((flag->vp_integer & 0x02) != 0) && /* accounting */
+ ((flag->vp_integer & 0xe0) == 0)) { /* not client, server or home-server */
request_stats_addvp(request, acctvp, &radius_acct_stats);
}
#endif
@@ -544,8 +543,8 @@ void request_stats_reply(REQUEST *request)
/*
* Proxied authentication requests.
*/
- if (((flag->vp_integer & 0x04) != 0) &&
- ((flag->vp_integer & 0x20) == 0)) {
+ if (((flag->vp_integer & 0x04) != 0) && /* proxy-auth */
+ ((flag->vp_integer & 0x20) == 0)) { /* not client */
request_stats_addvp(request, proxy_authvp, &proxy_auth_stats);
}
@@ -553,8 +552,8 @@ void request_stats_reply(REQUEST *request)
/*
* Proxied accounting requests.
*/
- if (((flag->vp_integer & 0x08) != 0) &&
- ((flag->vp_integer & 0x20) == 0)) {
+ if (((flag->vp_integer & 0x08) != 0) && /* proxy-accounting */
+ ((flag->vp_integer & 0x20) == 0)) { /* not client */
request_stats_addvp(request, proxy_acctvp, &proxy_acct_stats);
}
#endif
@@ -563,7 +562,7 @@ void request_stats_reply(REQUEST *request)
/*
* Internal server statistics
*/
- if ((flag->vp_integer & 0x10) != 0) {
+ if ((flag->vp_integer & 0x10) != 0) { /* internal */
vp = radius_pair_create(request->reply, &request->reply->vps,
PW_FREERADIUS_STATS_START_TIME, VENDORPEC_FREERADIUS);
if (vp) vp->vp_date = start_time.tv_sec;
@@ -607,7 +606,7 @@ void request_stats_reply(REQUEST *request)
/*
* For a particular client.
*/
- if ((flag->vp_integer & 0x20) != 0) {
+ if ((flag->vp_integer & 0x20) != 0) { /* client */
fr_ipaddr_t ipaddr;
VALUE_PAIR *server_ip, *server_port = NULL;
RADCLIENT *client = NULL;
@@ -764,8 +763,8 @@ void request_stats_reply(REQUEST *request)
/*
* For a particular "listen" socket.
*/
- if (((flag->vp_integer & 0x40) != 0) &&
- ((flag->vp_integer & 0x03) != 0)) {
+ if (((flag->vp_integer & 0x40) != 0) && /* server */
+ ((flag->vp_integer & 0x03) != 0)) { /* auth or accounting */
rad_listen_t *this;
VALUE_PAIR *server_ip, *server_port;
fr_ipaddr_t ipaddr;
@@ -807,7 +806,7 @@ void request_stats_reply(REQUEST *request)
fr_pair_add(&request->reply->vps,
fr_pair_copy(request->reply, server_port));
- if ((flag->vp_integer & 0x01) != 0) {
+ if ((flag->vp_integer & 0x01) != 0) { /* auth */
if ((request->listener->type == RAD_LISTEN_AUTH) ||
(request->listener->type == RAD_LISTEN_NONE)) {
request_stats_addvp(request, authvp, &this->stats);
@@ -817,7 +816,7 @@ void request_stats_reply(REQUEST *request)
}
#ifdef WITH_ACCOUNTING
- if ((flag->vp_integer & 0x02) != 0) {
+ if ((flag->vp_integer & 0x02) != 0) { /* accounting */
if ((request->listener->type == RAD_LISTEN_ACCT) ||
(request->listener->type == RAD_LISTEN_NONE)) {
request_stats_addvp(request, acctvp, &this->stats);
@@ -832,8 +831,8 @@ void request_stats_reply(REQUEST *request)
/*
* Home servers.
*/
- if (((flag->vp_integer & 0x80) != 0) &&
- ((flag->vp_integer & 0x03) != 0)) {
+ if (((flag->vp_integer & 0x80) != 0) && /* home-server */
+ ((flag->vp_integer & 0x03) != 0)) { /* auth or accounting */
home_server_t *home;
VALUE_PAIR *server_ip, *server_port;
fr_ipaddr_t ipaddr;
@@ -935,7 +934,7 @@ void request_stats_reply(REQUEST *request)
PW_FREERADIUS_STATS_LAST_PACKET_SENT, VENDORPEC_FREERADIUS);
if (vp) vp->vp_date = home->last_packet_sent;
- if ((flag->vp_integer & 0x01) != 0) {
+ if ((flag->vp_integer & 0x01) != 0) { /* auth */
if (home->type == HOME_TYPE_AUTH) {
request_stats_addvp(request, proxy_authvp,
&home->stats);
@@ -945,7 +944,7 @@ void request_stats_reply(REQUEST *request)
}
#ifdef WITH_ACCOUNTING
- if ((flag->vp_integer & 0x02) != 0) {
+ if ((flag->vp_integer & 0x02) != 0) { /* accounting */
if (home->type == HOME_TYPE_ACCT) {
request_stats_addvp(request, proxy_acctvp,
&home->stats);
@@ -991,14 +990,14 @@ void radius_stats_ema(fr_stats_ema_t *ema,
}
- tdiff = start->tv_sec;
- tdiff -= end->tv_sec;
+ tdiff = end->tv_sec;
+ tdiff -= start->tv_sec;
micro = (int) tdiff;
if (micro > 40) micro = 40; /* don't overflow 32-bit ints */
micro *= USEC;
- micro += start->tv_usec;
- micro -= end->tv_usec;
+ micro += end->tv_usec;
+ micro -= start->tv_usec;
micro *= EMA_SCALE;
diff --git a/src/main/threads.c b/src/main/threads.c
index a187106..5730b5e 100644
--- a/src/main/threads.c
+++ b/src/main/threads.c
@@ -291,7 +291,7 @@ static void tls_mutexes_destroy(void)
#ifdef HAVE_CRYPTO_SET_LOCKING_CALLBACK
int i, num;
- rad_assert(ssl_mutex != NULL);
+ rad_assert(ssl_mutexes != NULL);
num = CRYPTO_num_locks();
diff --git a/src/main/tls.c b/src/main/tls.c
index c8cae3b..736ee41 100644
--- a/src/main/tls.c
+++ b/src/main/tls.c
@@ -404,7 +404,7 @@ static unsigned int psk_server_callback(SSL *ssl, const char *identity,
* The passed identity is weird. Deny it.
*/
if (!identity_is_safe(identity)) {
- RWDEBUG("(TLS) Invalid characters in PSK identity %s", identity);
+ RWDEBUG("(TLS) %s - Invalid characters in PSK identity %s", conf->name, identity);
return 0;
}
@@ -421,7 +421,7 @@ static unsigned int psk_server_callback(SSL *ssl, const char *identity,
hex_len = radius_xlat(buffer, sizeof(buffer), request, conf->psk_query,
NULL, NULL);
if (!hex_len) {
- RWDEBUG("(TLS) PSK expansion returned an empty string.");
+ RWDEBUG("(TLS) %s - PSK expansion returned an empty string.", conf->name);
return 0;
}
@@ -431,7 +431,7 @@ static unsigned int psk_server_callback(SSL *ssl, const char *identity,
* the truncation, and complain about it.
*/
if (hex_len > (2 * max_psk_len)) {
- RWDEBUG("(TLS) Returned PSK is too long (%u > %u)",
+ RWDEBUG("(TLS) %s - Returned PSK is too long (%u > %u)", conf->name,
(unsigned int) hex_len, 2 * max_psk_len);
return 0;
}
@@ -635,9 +635,11 @@ tls_session_t *tls_new_client_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *con
case SSL_ERROR_WANT_READ:
ssn->connected = false;
+ RDEBUG("(TLS) %s - tls_new_client_session WANT_READ", conf->name);
return ssn;
case SSL_ERROR_WANT_WRITE:
+ RDEBUG("(TLS) %s - tls_new_client_session WANT_WRITE", conf->name);
ssn->connected = false;
return ssn;
}
@@ -681,7 +683,7 @@ tls_session_t *tls_new_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *conf, REQU
rad_assert(request != NULL);
- RDEBUG2("(TLS) Initiating new session");
+ RDEBUG2("(TLS) %s -Initiating new session", conf->name);
/*
* Replace X509 store if it is time to update CRLs/certs in ca_path
@@ -690,10 +692,10 @@ tls_session_t *tls_new_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *conf, REQU
pthread_mutex_lock(&conf->mutex);
/* recheck conf->ca_path_last_reload because it may be inaccurate without mutex */
if (conf->ca_path_last_reload + conf->ca_path_reload_interval <= request->timestamp) {
- RDEBUG2("Flushing X509 store to re-read data from ca_path dir");
+ RDEBUG2("(TLS) Flushing X509 store to re-read data from ca_path dir");
if ((new_cert_store = fr_init_x509_store(conf)) == NULL) {
- RERROR("(TLS) Error replacing X509 store, out of memory (?)");
+ RERROR("(TLS) %s - Error replacing X509 store, out of memory (?)", conf->name);
} else {
if (conf->old_x509_store) X509_STORE_free(conf->old_x509_store);
/*
@@ -752,7 +754,7 @@ tls_session_t *tls_new_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *conf, REQU
SSL_set_app_data(new_tls, NULL);
if ((state = talloc_zero(ctx, tls_session_t)) == NULL) {
- RERROR("(TLS) Error allocating memory for SSL state");
+ RERROR("(TLS) %s - Error allocating memory for SSL state", conf->name);
return NULL;
}
session_init(state);
@@ -808,7 +810,7 @@ tls_session_t *tls_new_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *conf, REQU
VALUE_PAIR *key = fr_pair_find_by_num(request->config, PW_TLS_SESSION_CERT_PRIVATE_KEY_FILE, 0, TAG_ANY);
if (!key) key = vp;
- RDEBUG2("(TLS) Loading session certificate file \"%s\"", vp->vp_strvalue);
+ RDEBUG2("(TLS) %s - Loading session certificate file \"%s\"", conf->name, vp->vp_strvalue);
if (conf->realms) {
fr_realm_ctx_t my_r, *r;
@@ -887,11 +889,18 @@ after_chain:
* Verify the peer certificate, if asked.
*/
if (client_cert) {
- RDEBUG2("(TLS) Setting verify mode to require certificate from client");
+ RDEBUG2("(TLS) %s - Setting verify mode to require certificate from client", conf->name);
verify_mode = SSL_VERIFY_PEER;
verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
verify_mode |= SSL_VERIFY_CLIENT_ONCE;
}
+#ifdef PSK_MAX_IDENTITY_LEN
+ else if (conf->psk_identity) {
+ RDEBUG2("(TLS) %s - Setting verify peer mode due to PSK", conf->name);
+ verify_mode = SSL_VERIFY_PEER;
+ verify_mode |= SSL_VERIFY_CLIENT_ONCE;
+ }
+#endif
SSL_set_verify(state->ssl, verify_mode, cbtls_verify);
SSL_set_ex_data(state->ssl, FR_TLS_EX_INDEX_CONF, (void *)conf);
@@ -970,14 +979,14 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn)
int err;
if (ssn->invalid_hb_used) {
- REDEBUG("(TLS) OpenSSL Heartbeat attack detected. Closing connection");
+ REDEBUG("(TLS) %s - OpenSSL Heartbeat attack detected. Closing connection", ssn->conf->name);
return 0;
}
if (ssn->dirty_in.used > 0) {
err = BIO_write(ssn->into_ssl, ssn->dirty_in.data, ssn->dirty_in.used);
if (err != (int) ssn->dirty_in.used) {
- REDEBUG("(TLS) Failed writing %zd bytes to SSL BIO: %d", ssn->dirty_in.used, err);
+ REDEBUG("(TLS) %s - Failed writing %zd bytes to SSL BIO: %d", ssn->conf->name, ssn->dirty_in.used, err);
record_init(&ssn->dirty_in);
return 0;
}
@@ -998,7 +1007,7 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn)
VALUE_PAIR *vp;
char const *str_version;
- RDEBUG2("(TLS) Connection Established");
+ RDEBUG2("(TLS) %s - Connection Established", ssn->conf->name);
ssn->is_init_finished = true;
vp = fr_pair_afrom_num(request->state_ctx, PW_TLS_SESSION_CIPHER_SUITE, 0);
@@ -1049,10 +1058,10 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn)
REXDENT();
}
}
- else if (SSL_in_init(ssn->ssl)) { RDEBUG2("(TLS) In Handshake Phase"); }
- else if (SSL_in_before(ssn->ssl)) { RDEBUG2("(TLS) Before Handshake Phase"); }
- else if (SSL_in_accept_init(ssn->ssl)) { RDEBUG2("(TLS) In Accept mode"); }
- else if (SSL_in_connect_init(ssn->ssl)) { RDEBUG2("(TLS) In Connect mode"); }
+ else if (SSL_in_init(ssn->ssl)) { RDEBUG2("(TLS) %s - In Handshake Phase", ssn->conf->name); }
+ else if (SSL_in_before(ssn->ssl)) { RDEBUG2("(TLS) %s - Before Handshake Phase", ssn->conf->name); }
+ else if (SSL_in_accept_init(ssn->ssl)) { RDEBUG2("(TLS) %s- In Accept mode", ssn->conf->name); }
+ else if (SSL_in_connect_init(ssn->ssl)) { RDEBUG2("(TLS) %s - In Connect mode", ssn->conf->name); }
#if OPENSSL_VERSION_NUMBER >= 0x10001000L
/*
@@ -1070,7 +1079,7 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn)
* to get the session is a hard fail.
*/
if (!ssn->ssl_session && ssn->is_init_finished) {
- RDEBUG("(TLS) Failed getting session");
+ RDEBUG("(TLS) %s - Failed getting session", ssn->conf->name);
return 0;
}
}
@@ -1084,12 +1093,12 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn)
err = BIO_read(ssn->from_ssl, ssn->dirty_out.data,
sizeof(ssn->dirty_out.data));
if (err > 0) {
- RDEBUG3("(TLS) got %d bytes of data", err);
+ RDEBUG3("(TLS) %s- got %d bytes of data", ssn->conf->name, err);
ssn->dirty_out.used = err;
} else if (BIO_should_retry(ssn->from_ssl)) {
record_init(&ssn->dirty_in);
- RDEBUG2("(TLS) Asking for more data in tunnel.");
+ RDEBUG2("(TLS) %s - Asking for more data in tunnel.", ssn->conf->name);
return 1;
} else {
@@ -1098,7 +1107,7 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn)
return 0;
}
} else {
- RDEBUG2("(TLS) Application data.");
+ RDEBUG2("(TLS) %s - Application data.", ssn->conf->name);
/* Its clean application data, leave whatever is in the buffer */
#if 0
record_init(&ssn->clean_out);
@@ -1245,6 +1254,7 @@ void tls_session_information(tls_session_t *tls_session)
REQUEST *request;
VALUE_PAIR *vp;
char content_type[16], alert_buf[16];
+ char name_buf[128];
char buffer[32];
/*
@@ -1262,7 +1272,12 @@ void tls_session_information(tls_session_t *tls_session)
request = SSL_get_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST);
if (!request) return;
- str_write_p = tls_session->info.origin ? "(TLS) send" : "(TLS) recv";
+ if (tls_session->info.origin) {
+ snprintf(name_buf, sizeof(name_buf), "(TLS) %s - send", tls_session->conf->name);
+ } else {
+ snprintf(name_buf, sizeof(name_buf), "(TLS) %s - recv", tls_session->conf->name);
+ }
+ str_write_p = name_buf;
#define FROM_CLIENT (tls_session->info.origin == 0)
@@ -1605,7 +1620,7 @@ void tls_session_information(tls_session_t *tls_session)
RDEBUG2("%s", tls_session->info.info_description);
- if (FROM_CLIENT && details) RDEBUG2("(TLS) The client is informing us that %s.", details);
+ if (FROM_CLIENT && details) RDEBUG2("(TLS) %s - The client is informing us that %s.", tls_session->conf->name, details);
}
static CONF_PARSER cache_config[] = {
@@ -1735,6 +1750,10 @@ static CONF_PARSER tls_client_config[] = {
{ "certificate_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, certificate_file), NULL },
{ "ca_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, ca_file), NULL },
{ "private_key_password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, fr_tls_server_conf_t, private_key_password), NULL },
+#ifdef PSK_MAX_IDENTITY_LEN
+ { "psk_identity", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, psk_identity), NULL },
+ { "psk_hexphrase", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, fr_tls_server_conf_t, psk_password), NULL },
+#endif
{ "dh_file", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, dh_file), NULL },
{ "random_file", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, random_file), NULL },
{ "fragment_size", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, fragment_size), "1024" },
@@ -1924,7 +1943,7 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess)
blob_len = i2d_SSL_SESSION(sess, NULL);
if (blob_len < 1) {
/* something went wrong */
- if (request) RWDEBUG("(TLS) Session serialisation failed, could not determine required buffer length");
+ if (request) RWDEBUG("(TLS) %s - Session serialisation failed, could not determine required buffer length", conf->name);
return 0;
}
@@ -1932,14 +1951,14 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess)
/* alloc and convert to ASN.1 */
sess_blob = malloc(blob_len);
if (!sess_blob) {
- RWDEBUG("(TLS) Session serialisation failed, couldn't allocate buffer (%d bytes)", blob_len);
+ RWDEBUG("(TLS) %s - Session serialisation failed, couldn't allocate buffer (%d bytes)", conf->name, blob_len);
return 0;
}
/* openssl mutates &p */
p = sess_blob;
rv = i2d_SSL_SESSION(sess, &p);
if (rv != blob_len) {
- if (request) RWDEBUG("(TLS) Session serialisation failed");
+ if (request) RWDEBUG("(TLS) %s - Session serialisation failed", conf->name);
goto error;
}
@@ -1948,8 +1967,8 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess)
conf->session_cache_path, FR_DIR_SEP, buffer);
fd = open(filename, O_RDWR|O_CREAT|O_EXCL, S_IWUSR);
if (fd < 0) {
- if (request) RERROR("(TLS) Session serialisation failed, failed opening session file %s: %s",
- filename, fr_syserror(errno));
+ if (request) RERROR("(TLS) %s - Session serialisation failed, failed opening session file %s: %s",
+ conf->name, filename, fr_syserror(errno));
goto error;
}
@@ -1971,7 +1990,7 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess)
while (todo > 0) {
rv = write(fd, p, todo);
if (rv < 1) {
- if (request) RWDEBUG("(TLS) Failed writing session: %s", fr_syserror(errno));
+ if (request) RWDEBUG("(TLS) %s - Failed writing session: %s", conf->name, fr_syserror(errno));
close(fd);
goto error;
}
@@ -1979,7 +1998,7 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess)
todo -= rv;
}
close(fd);
- if (request) RWDEBUG("(TLS) Wrote session %s to %s (%d bytes)", buffer, filename, blob_len);
+ if (request) RWDEBUG("(TLS) %s - Wrote session %s to %s (%d bytes)", conf->name, buffer, filename, blob_len);
}
error:
@@ -2102,20 +2121,20 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl, const unsigned char *data, int l
snprintf(filename, sizeof(filename), "%s%c%s.asn1", conf->session_cache_path, FR_DIR_SEP, buffer);
fd = open(filename, O_RDONLY);
if (fd < 0) {
- RWDEBUG("(TLS) No persisted session file %s: %s", filename, fr_syserror(errno));
+ RWDEBUG("(TLS) %s - No persisted session file %s: %s", conf->name, filename, fr_syserror(errno));
goto error;
}
rv = fstat(fd, &st);
if (rv < 0) {
- RWDEBUG("(TLS) Failed stating persisted session file %s: %s", filename, fr_syserror(errno));
+ RWDEBUG("(TLS) %s - Failed stating persisted session file %s: %s", conf->name, filename, fr_syserror(errno));
close(fd);
goto error;
}
sess_data = talloc_array(NULL, unsigned char, st.st_size);
if (!sess_data) {
- RWDEBUG("(TLS) Failed allocating buffer for persisted session (%d bytes)", (int) st.st_size);
+ RWDEBUG("(TLS) %s- Failed allocating buffer for persisted session (%d bytes)", conf->name, (int) st.st_size);
close(fd);
goto error;
}
@@ -2125,7 +2144,7 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl, const unsigned char *data, int l
while (todo > 0) {
rv = read(fd, q, todo);
if (rv < 1) {
- RWDEBUG("(TLS) Failed reading persisted session: %s", fr_syserror(errno));
+ RWDEBUG("(TLS) %s - Failed reading persisted session: %s", conf->name, fr_syserror(errno));
close(fd);
goto error;
}
@@ -2149,7 +2168,7 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl, const unsigned char *data, int l
memcpy(&o, &p, sizeof(o));
sess = d2i_SSL_SESSION(NULL, o, st.st_size);
if (!sess) {
- RWDEBUG("(TLS) Failed loading persisted session: %s", ERR_error_string(ERR_get_error(), NULL));
+ RWDEBUG("(TLS) %s - Failed loading persisted session: %s", conf->name, ERR_error_string(ERR_get_error(), NULL));
goto error;
}
@@ -2159,7 +2178,7 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl, const unsigned char *data, int l
rv = pairlist_read(talloc_ctx, filename, &pairlist, 1);
if (rv < 0) {
/* not safe to un-persist a session w/o VPs */
- RWDEBUG("(TLS) Failed loading persisted VPs for session %s", buffer);
+ RWDEBUG("(TLS) %s - Failed loading persisted VPs for session %s", conf->name, buffer);
SSL_SESSION_free(sess);
sess = NULL;
goto error;
@@ -2173,7 +2192,7 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl, const unsigned char *data, int l
time_t expires;
if (ocsp_asn1time_to_epoch(&expires, vp->vp_strvalue) < 0) {
- RDEBUG2("Failed getting certificate expiration, removing cache entry for session %s - %s", buffer, fr_strerror());
+ RDEBUG2("(TLS) %s - Failed getting certificate expiration, removing cache entry for session %s - %s", conf->name, buffer, fr_strerror());
SSL_SESSION_free(sess);
sess = NULL;
goto error;
@@ -2193,8 +2212,8 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl, const unsigned char *data, int l
if (vp) {
if ((request->timestamp + vp->vp_integer) > expires) {
vp->vp_integer = expires - request->timestamp;
- RWDEBUG2("(TLS) Updating Session-Timeout to %u, due to impending certificate expiration",
- vp->vp_integer);
+ RWDEBUG2("(TLS) %s - Updating Session-Timeout to %u, due to impending certificate expiration",
+ conf->name, vp->vp_integer);
}
}
}
@@ -2208,8 +2227,8 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl, const unsigned char *data, int l
VALUE_PAIR *type = fr_pair_find_by_num(request->packet->vps, PW_EAP_TYPE, 0, TAG_ANY);
if (type && (type->vp_integer != vp->vp_integer)) {
- REDEBUG("Resumption has changed EAP types for session %s", buffer);
- REDEBUG("Rejecting session due to protocol violations");
+ REDEBUG("(TLS) %s - Resumption has changed EAP types for session %s", conf->name, buffer);
+ REDEBUG("(TLS) %s - Rejecting session due to protocol violations", conf->name);
goto error;
}
}
@@ -2524,8 +2543,8 @@ static SSL_SESSION *cbtls_cache_load(SSL *ssl, const unsigned char *data, int le
if (vp) {
if ((request->timestamp + vp->vp_integer) > expires) {
vp->vp_integer = expires - request->timestamp;
- RWDEBUG2("(TLS) Updating Session-Timeout to %u, due to impending certificate expiration",
- vp->vp_integer);
+ RWDEBUG2("(TLS) %s - Updating Session-Timeout to %u, due to impending certificate expiration",
+ conf->name, vp->vp_integer);
}
}
}
@@ -2535,7 +2554,7 @@ static SSL_SESSION *cbtls_cache_load(SSL *ssl, const unsigned char *data, int le
*/
vp = fr_pair_find_by_num(fake->state, PW_TLS_SESSION_DATA, 0, TAG_ANY);
if (!vp) {
- RWDEBUG("(TLS) Failed to find TLS-Session-Data in 'session-state' list for session %s", buffer);
+ RWDEBUG("(TLS) %s - Failed to find TLS-Session-Data in 'session-state' list for session %s", conf->name, buffer);
goto error;
}
@@ -2552,7 +2571,7 @@ static SSL_SESSION *cbtls_cache_load(SSL *ssl, const unsigned char *data, int le
p = vp->vp_octets;
sess = d2i_SSL_SESSION(NULL, &p, vp->vp_length);
if (!sess) {
- RWDEBUG("(TLS) Failed loading persisted session: %s", ERR_error_string(ERR_get_error(), NULL));
+ RWDEBUG("(TLS) %s - Failed loading persisted session: %s", conf->name, ERR_error_string(ERR_get_error(), NULL));
goto error;
}
@@ -2629,7 +2648,7 @@ typedef enum {
} ocsp_status_t;
static ocsp_status_t ocsp_check(REQUEST *request, X509_STORE *store, X509 *issuer_cert, X509 *client_cert,
- fr_tls_server_conf_t *conf)
+ STACK_OF(X509) *untrusted, fr_tls_server_conf_t *conf)
{
OCSP_CERTID *certid;
OCSP_REQUEST *req;
@@ -2810,7 +2829,7 @@ static ocsp_status_t ocsp_check(REQUEST *request, X509_STORE *store, X509 *issue
REDEBUG("ocsp: Response has wrong nonce value");
goto ocsp_end;
}
- if (OCSP_basic_verify(bresp, NULL, store, 0)!=1){
+ if (OCSP_basic_verify(bresp, untrusted, store, 0)!=1){
REDEBUG("ocsp: Couldn't verify OCSP basic response");
goto ocsp_end;
}
@@ -2931,10 +2950,6 @@ static char const *cert_attr_names[9][2] = {
#define FR_TLS_SAN_UPN (7)
#define FR_TLS_VALID_SINCE (8)
-static const char *cert_names[2] = {
- "client", "server",
-};
-
/*
* Before trusting a certificate, you must make sure that the
* certificate is 'valid'. There are several steps that your
@@ -3048,7 +3063,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
buf[0] = '\0';
sn = X509_get_serialNumber(client_cert);
- RDEBUG2("(TLS) Creating attributes from %s certificate", cert_names[lookup ]);
+ RDEBUG2("(TLS) %s - Creating attributes from %d certificate in chain", conf->name, lookup + 1);
RINDENT();
/*
@@ -3340,8 +3355,8 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
#if 0
ASN1_TIME_print(bio_err, X509_get_notAfter(ctx->current_cert));
-#endif
break;
+#endif
}
/*
@@ -3366,8 +3381,10 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
if (conf->disallow_untrusted || RDEBUG_ENABLED2) {
int i;
- WARN("Certificate chain - %i cert(s) untrusted",
+ WARN("Certificate chain - %i intermediate CA cert(s) untrusted",
X509_STORE_CTX_get_num_untrusted(ctx));
+ WARN("To forbid these certificates see 'reject_unknown_intermediate_ca'");
+
for (i = sk_X509_num(untrusted); i > 0 ; i--) {
X509 *this_cert = sk_X509_value(untrusted, i - 1);
@@ -3417,7 +3434,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
}
} /* check_cert_cn */
-#ifdef HAVE_OPENSSL_OCSP_H
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && defined(HAVE_OPENSSL_OCSP_H)
if (my_ok) {
/*
* No OCSP, allow external verification.
@@ -3447,7 +3464,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx)
* run the external verification routine. If it's marked as
* "skip verify on OK", then we don't do verify.
*/
- my_ok = ocsp_check(request, ocsp_store, issuer_cert, client_cert, conf);
+ my_ok = ocsp_check(request, ocsp_store, issuer_cert, client_cert, untrusted, conf);
if (my_ok != OCSP_STATUS_FAILED) {
do_verify = !conf->verify_skip_if_ocsp_ok;
}
@@ -4161,6 +4178,16 @@ post_ca:
ERROR("Unknown or unsupported value for tls_min_version '%s'", conf->tls_min_version);
return NULL;
}
+
+#ifdef WITH_RADIUSV11
+ /*
+ * RADIUS 1.1 requires TLS 1.3 or later.
+ */
+ if (conf->radiusv11 && (min_version < TLS1_3_VERSION)) {
+ WARN(LOG_PREFIX ": The configuration allows TLS <1.3. RADIUS/1.1 MUST use TLS 1.3");
+ WARN(LOG_PREFIX ": Please set: tls_min_version = '1.3'");
+ }
+#endif
} else {
#ifdef WITH_RADIUSV11
/*
diff --git a/src/main/tls_listen.c b/src/main/tls_listen.c
index fa8c382..3dc786b 100644
--- a/src/main/tls_listen.c
+++ b/src/main/tls_listen.c
@@ -377,7 +377,6 @@ static int tls_socket_recv(rad_listen_t *listener)
REQUEST *request;
listen_socket_t *sock = listener->data;
fr_tls_status_t status;
- RADCLIENT *client = sock->client;
if (!sock->packet) {
sock->packet = rad_alloc(sock, false);
@@ -580,6 +579,7 @@ check_for_setup:
* or any other contents.
*/
request->packet->code = PW_CODE_STATUS_SERVER;
+ request->packet->id = request->reply->id = 0;
request->packet->data = talloc_zero_array(request->packet, uint8_t, 20);
request->packet->data[0] = PW_CODE_STATUS_SERVER;
request->packet->data[3] = 20;
@@ -673,6 +673,7 @@ read_application_data:
#ifdef WITH_RADIUSV11
packet->radiusv11 = sock->radiusv11;
#endif
+ packet->tls = true;
if (!rad_packet_ok(packet, 0, NULL)) {
if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror());
@@ -708,8 +709,6 @@ read_application_data:
}
}
- FR_STATS_INC(auth, total_requests);
-
return 1;
}
@@ -874,6 +873,7 @@ int dual_tls_send(rad_listen_t *listener, REQUEST *request)
*/
if (sock->state == LISTEN_TLS_CHECKING) {
if (request->reply->code != PW_CODE_ACCESS_ACCEPT) {
+ RDEBUG("(TLS) Connection checks failed - closing connection");
listener->status = RAD_LISTEN_STATUS_EOL;
listener->tls = NULL; /* parent owns this! */
@@ -887,6 +887,7 @@ int dual_tls_send(rad_listen_t *listener, REQUEST *request)
/*
* Resume reading from the listener.
*/
+ RDEBUG("(TLS) Connection checks succeeded - continuing with normal reads");
listener->status = RAD_LISTEN_STATUS_RESUME;
radius_update_listener(listener);
@@ -1286,6 +1287,7 @@ int proxy_tls_recv(rad_listen_t *listener)
}
#endif
+ packet->tls = true;
/*
* FIXME: Client MIB updates?
@@ -1373,6 +1375,7 @@ int proxy_tls_send(rad_listen_t *listener, REQUEST *request)
* if there's no packet, encode it here.
*/
if (!request->proxy->data) {
+ request->reply->tls = true;
request->proxy_listener->proxy_encode(request->proxy_listener,
request);
}
@@ -1406,9 +1409,11 @@ int proxy_tls_send(rad_listen_t *listener, REQUEST *request)
return -1;
}
+ RDEBUG3("(TLS) has %zu bytes in the buffer", sock->ssn->clean_out.used);
+
memcpy(sock->ssn->clean_out.data + sock->ssn->clean_out.used, request->proxy->data, request->proxy->data_len);
sock->ssn->clean_out.used += request->proxy->data_len;
- RDEBUG3("(TLS) Writing %zu bytes for later (total %zu)", request->proxy->data_len, sock->ssn->clean_out.used);
+ RDEBUG3("(TLS) Saving %zu bytes of RADIUS traffic for later (total %zu)", request->proxy->data_len, sock->ssn->clean_out.used);
PTHREAD_MUTEX_UNLOCK(&sock->mutex);
return 0;
@@ -1508,6 +1513,8 @@ int proxy_tls_send_reply(rad_listen_t *listener, REQUEST *request)
if ((listener->status != RAD_LISTEN_STATUS_INIT &&
(listener->status != RAD_LISTEN_STATUS_KNOWN))) return 0;
+ request->reply->tls = true;
+
/*
* Pack the VPs
*/
diff --git a/src/main/tmpl.c b/src/main/tmpl.c
index 6ec2598..6746bde 100644
--- a/src/main/tmpl.c
+++ b/src/main/tmpl.c
@@ -579,6 +579,7 @@ ssize_t tmpl_from_attr_substr(vp_tmpl_t *vpt, char const *name,
long num;
char *q;
tmpl_type_t type = TMPL_TYPE_ATTR;
+ DICT_ATTR const *da;
value_pair_tmpl_attr_t attr; /* So we don't fill the tmpl with junk and then error out */
@@ -694,6 +695,16 @@ ssize_t tmpl_from_attr_substr(vp_tmpl_t *vpt, char const *name,
}
/*
+ * Canonicalize the attribute.
+ *
+ * We can define multiple names for one attribute. In
+ * which case we only use the canonical name.
+ */
+ da = dict_attrbyvalue(attr.da->attr, attr.da->vendor);
+ if (da && (attr.da != da)) attr.da = da;
+
+
+ /*
* The string MIGHT have a tag.
*/
if (*p == ':') {
diff --git a/src/main/unittest.c b/src/main/unittest.c
index 72fdadc..c82d31d 100644
--- a/src/main/unittest.c
+++ b/src/main/unittest.c
@@ -55,7 +55,7 @@ char const *radiusd_version = "FreeRADIUS Version " RADIUSD_VERSION_STRING
#endif
;
-fr_event_list_t *el = NULL;
+static fr_event_list_t *el = NULL;
/*
* Static functions.
diff --git a/src/main/util.c b/src/main/util.c
index b216cc9..607bcaa 100644
--- a/src/main/util.c
+++ b/src/main/util.c
@@ -398,15 +398,15 @@ size_t rad_filename_escape(UNUSED REQUEST *request, char *out, size_t outlen, ch
switch (utf8_len) {
case 2:
- snprintf(out, freespace, "-%x-%x", in[0], in[1]);
+ snprintf(out, freespace, "-%x-%x", (uint8_t)in[0], (uint8_t)in[1]);
break;
case 3:
- snprintf(out, freespace, "-%x-%x-%x", in[0], in[1], in[2]);
+ snprintf(out, freespace, "-%x-%x-%x", (uint8_t)in[0], (uint8_t)in[1], (uint8_t)in[2]);
break;
case 4:
- snprintf(out, freespace, "-%x-%x-%x-%x", in[0], in[1], in[2], in[3]);
+ snprintf(out, freespace, "-%x-%x-%x-%x", (uint8_t)in[0], (uint8_t)in[1], (uint8_t)in[2], (uint8_t)in[3]);
break;
}
diff --git a/src/main/version.c b/src/main/version.c
index 2fe3428..c190337 100644
--- a/src/main/version.c
+++ b/src/main/version.c
@@ -613,7 +613,7 @@ void version_print(void)
DEBUG2(" ");
}
INFO("FreeRADIUS Version " RADIUSD_VERSION_STRING);
- INFO("Copyright (C) 1999-2022 The FreeRADIUS server project and contributors");
+ INFO("Copyright (C) 1999-2023 The FreeRADIUS server project and contributors");
INFO("There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A");
INFO("PARTICULAR PURPOSE");
INFO("You may redistribute copies of FreeRADIUS under the terms of the");