include $(TOPDIR)/rules.mk
PKG_NAME:=freeradius3
-PKG_VERSION:=3.2.8
+PKG_VERSION:=3.2.10
PKG_VERSION_UNDERSCORE:=$(subst .,_,${PKG_VERSION})
PKG_RELEASE:=1
PKG_SOURCE:=freeradius-server-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://github.com/FreeRADIUS/freeradius-server/releases/download/release_$(PKG_VERSION_UNDERSCORE)/
-PKG_HASH:=3846498103d3faf182abd968a440128cb0c5589330bdcdd58c466ef15d9f9fdf
+PKG_HASH:=40e0cdfdcceb22cf0acb79bc29cf7c32995466a61fda09445ce5220608a55afd
PKG_MAINTAINER:=Alexandru Ardelean <ardeleanalex@gmail.com>
PKG_LICENSE:=GPL-2.0
define Package/freeradius3-common
$(call Package/freeradius3/Default)
TITLE:=common files
- DEPENDS:= +USE_GLIBC:libcrypt-compat +USE_GLIBC:libpthread +USE_GLIBC:libbsd +FREERADIUS3_OPENSSL:libopenssl +libcap +libpcap +libncurses +libreadline +libtalloc +libatomic
+ DEPENDS:=+USE_GLIBC:libcrypt-compat +USE_GLIBC:libpthread +USE_GLIBC:libbsd +FREERADIUS3_OPENSSL:libopenssl +FREERADIUS3_OPENSSL:libopenssl-legacy +libcap +libpcap +libncurses +libreadline +libtalloc +libatomic
endef
define Package/freeradius3-default
+++ /dev/null
-Description: disable session caching in the server (as opposed to in the
- config, which would be way harder to get right) to address
- https://security-tracker.debian.org/tracker/CVE-2017-9148
-Author: Michael Stapelberg <stapelberg@debian.org>
-Forwarded: not-needed
-Last-Update: 2020-04-28
-
----
-
---- a/src/main/tls.c
-+++ b/src/main/tls.c
-@@ -860,7 +860,7 @@ after_chain:
- }
- if (vp) vp->vp_integer = state->mtu;
-
-- if (conf->session_cache_enable) state->allow_session_resumption = true; /* otherwise it's false */
-+ if (/*conf->session_cache_enable*/0) state->allow_session_resumption = true; /* otherwise it's false */
-
- return state;
- }
-@@ -4520,7 +4520,7 @@ post_ca:
- /*
- * Callbacks, etc. for session resumption.
- */
-- if (conf->session_cache_enable) {
-+ if (/*conf->session_cache_enable*/0) {
- /*
- * Cache sessions on disk if requested.
- */
-@@ -4600,7 +4600,7 @@ post_ca:
- /*
- * Setup session caching
- */
-- if (conf->session_cache_enable) {
-+ if (/*conf->session_cache_enable*/0) {
- /*
- * Create a unique context Id per EAP-TLS configuration.
- */
-@@ -4890,7 +4890,7 @@ fr_tls_server_conf_t *tls_server_conf_pa
- goto error;
- }
-
-- if (conf->session_cache_enable) {
-+ if (/*conf->session_cache_enable*/0) {
- CONF_SECTION *subcs;
- CONF_ITEM *ci;
-
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Alexandru Ardelean <alex@shruggie.ro>
+Date: Wed, 3 Jun 2026 01:48:03 +0300
+Subject: [PATCH] configure: assume __thread TLS works when cross-compiling
+
+OpenWrt cross-compiles with gcc on musl, which supports __thread. The
+AC_RUN_IFELSE check in fr_tls.m4 defaults to "no" for cross builds, so
+TLS_STORAGE_CLASS is never defined, __THREAD is never set, and threads.h
+falls through to '#error unsupported' (added in 3.2.9).
+
+Signed-off-by: Alexandru Ardelean <alex@shruggie.ro>
+---
+--- a/configure
++++ b/configure
+@@ -12625,7 +12625,7 @@ fi
+ printf %s "checking for __thread support in compiler... " >&6; }
+ if test "$cross_compiling" = yes
+ then :
+- have_tls=no
++ have_tls=yes
+ else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+ /* end confdefs.h. */
+++ /dev/null
---- a/src/include/threads.h
-+++ b/src/include/threads.h
-@@ -92,7 +92,7 @@ static _t __fr_thread_local_init_##_n(pt
- # define fr_thread_local_get(_n) _n
- #elif defined(HAVE_PTHREAD_H)
- # include <pthread.h>
--# define fr_thread_local_setup(_t, _n) \
-+# define fr_thread_local_setup(_t, _n) static __thread _t _n;\
- static pthread_key_t __fr_thread_local_key_##_n;\
- static pthread_once_t __fr_thread_local_once_##_n = PTHREAD_ONCE_INIT;\
- static pthread_destructor_t __fr_thread_local_destructor_##_n = NULL;\
-@@ -103,17 +103,17 @@ static void __fr_thread_local_destroy_##
- static void __fr_thread_local_key_init_##_n(void)\
- {\
- (void) pthread_key_create(&__fr_thread_local_key_##_n, __fr_thread_local_destroy_##_n);\
-- (void) pthread_setspecific(__fr_thread_local_key_##_n, &(_n));\
- }\
- static _t __fr_thread_local_init_##_n(pthread_destructor_t func)\
- {\
- __fr_thread_local_destructor_##_n = func;\
- if (_n) return _n; \
- (void) pthread_once(&__fr_thread_local_once_##_n, __fr_thread_local_key_init_##_n);\
-+ (void) pthread_setspecific(__fr_thread_local_key_##_n, &(_n));\
- return _n;\
- }
--# define fr_thread_local_init(_n, _f) __fr_thread_local_init_##_n(_f)
--# define fr_thread_local_set(_n, _v) __fr_thread_local_set_##_n(_v)
--# define fr_thread_local_get(_n) __fr_thread_local_get_##_n()
-+# define fr_thread_local_init(_n, _f) __fr_thread_local_init_##_n(_f)
-+# define fr_thread_local_set(_n, _v) ((int)!((_n = _v) || 1))
-+# define fr_thread_local_get(_n) _n
- #endif
- #endif
+++ /dev/null
---- a/src/main/radtest.in
-+++ b/src/main/radtest.in
-@@ -117,7 +117,7 @@ if [ "$7" ]
- then
- nas=$7
- else
-- nas=`(hostname || uname -n) 2>/dev/null | sed 1q`
-+ nas=$(cat /proc/sys/kernel/hostname)
- fi
-
- (
--- /dev/null
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Alexandru Ardelean <alex@shruggie.ro>
+Date: Wed, 3 Jun 2026 01:48:03 +0300
+Subject: [PATCH] radtest: use $HOSTNAME for the NAS name when it is set
+
+radtest derives the NAS name from `hostname` or `uname -n`, neither of
+which is guaranteed to be present in a minimal image. Prefer the
+$HOSTNAME environment variable when the shell exports it, and keep the
+original command substitution as the fallback.
+
+Signed-off-by: Alexandru Ardelean <alex@shruggie.ro>
+---
+--- a/src/main/radtest.in
++++ b/src/main/radtest.in
+@@ -117,7 +117,12 @@ if [ "$7" ]
+ then
+ nas=$7
+ else
+- nas=`(hostname || uname -n) 2>/dev/null | sed 1q`
++ if [ -n "$HOSTNAME" ]
++ then
++ nas=$HOSTNAME
++ else
++ nas=`(hostname || uname -n) 2>/dev/null | sed 1q`
++ fi
+ fi
+
+ (
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Rosen Penev <rosenp@gmail.com>
+Date: Sun, 2 Aug 2020 15:07:47 -0700
+Subject: [PATCH] fix compilation without deprecated OpenSSL APIs
+
+Guard the OpenSSL 1.0 locking callbacks and the explicit library
+init/cleanup calls behind a version check, switch X509_get_notAfter() and
+X509_get_notBefore() to their X509_get0_* replacements, replace the
+SSLeay() aliases with OpenSSL_version_num()/OpenSSL_version(), and
+include <openssl/dh.h> explicitly.
+
+Signed-off-by: Rosen Penev <rosenp@gmail.com>
+---
--- a/src/main/threads.c
+++ b/src/main/threads.c
-@@ -275,6 +275,7 @@ static void ssl_locking_function(int mod
+@@ -257,6 +257,7 @@ static void ssl_locking_function(int mod
*/
int tls_mutexes_init(void)
{
int i, num;
rad_assert(ssl_mutexes == NULL);
-@@ -292,6 +293,7 @@ int tls_mutexes_init(void)
+@@ -274,6 +275,7 @@ int tls_mutexes_init(void)
}
CRYPTO_set_locking_callback(ssl_locking_function);
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
# include <openssl/provider.h>
-@@ -2943,7 +2944,7 @@ int cbtls_verify(int ok, X509_STORE_CTX
+@@ -2985,7 +2986,7 @@ int cbtls_verify(int ok, X509_STORE_CTX
int my_ok = ok;
ASN1_INTEGER *sn = NULL;
VALUE_PAIR **certs;
char **identity;
#ifdef HAVE_OPENSSL_OCSP_H
-@@ -3035,7 +3036,7 @@ int cbtls_verify(int ok, X509_STORE_CTX
+@@ -3077,7 +3078,7 @@ int cbtls_verify(int ok, X509_STORE_CTX
* Get the Expiration Date
*/
buf[0] = '\0';
if (certs && (lookup <= 1) && asn_time &&
(asn_time->length < (int) sizeof(buf))) {
memcpy(buf, (char*) asn_time->data, asn_time->length);
-@@ -3048,7 +3049,7 @@ int cbtls_verify(int ok, X509_STORE_CTX
+@@ -3090,7 +3091,7 @@ int cbtls_verify(int ok, X509_STORE_CTX
* Get the Valid Since Date
*/
buf[0] = '\0';
if (certs && (lookup <= 1) && asn_time &&
(asn_time->length < (int) sizeof(buf))) {
memcpy(buf, (char*) asn_time->data, asn_time->length);
-@@ -3655,10 +3656,12 @@ static int set_ecdh_curve(SSL_CTX *ctx,
+@@ -3697,10 +3698,12 @@ static int set_ecdh_curve(SSL_CTX *ctx,
*/
int tls_global_init(TLS_UNUSED bool spawn_flag, TLS_UNUSED bool check)
{
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
EVP_set_default_properties(NULL, "-fips");
-@@ -3762,6 +3765,7 @@ int tls_global_version_check(char const
+@@ -3806,6 +3809,7 @@ int tls_global_version_check(char const
*/
void tls_global_cleanup(void)
{
#if OPENSSL_VERSION_NUMBER < 0x10000000L
ERR_remove_state(0);
#elif OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
-@@ -3787,6 +3791,7 @@ void tls_global_cleanup(void)
+@@ -3833,6 +3837,7 @@ void tls_global_cleanup(void)
ERR_free_strings();
EVP_cleanup();
CRYPTO_cleanup_all_ex_data();
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Alexandru Ardelean <alex@shruggie.ro>
+Date: Mon, 13 Jul 2026 22:18:27 +0300
+Subject: [PATCH] rlm_krb5: assume thread-safety when cross-compiling
+
+The autoconf probe for krb5_is_thread_safe() has no action-if-cross-compiling
+argument, so once libkrb5 provides the symbol configure aborts outright with
+"cannot run test program while cross compiling".
+
+Add the missing argument: warn that the probe was skipped, and assume
+thread-safety, which is correct for all modern krb5 builds.
+
+Signed-off-by: Alexandru Ardelean <alex@shruggie.ro>
+---
--- a/src/modules/rlm_krb5/configure
+++ b/src/modules/rlm_krb5/configure
-@@ -4003,12 +4003,6 @@ if test "x$smart_lib" != "x"; then
- fi
+@@ -4004,10 +4004,9 @@ fi
if test "x$ac_cv_lib_krb5_krb5_is_thread_safe" = xyes; then
-- if test "$cross_compiling" = yes; then :
+ if test "$cross_compiling" = yes; then :
- { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "cannot run test program while cross compiling
-See \`config.log' for more details" "$LINENO" 5; }
--else
++ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cross compiling: not checking" >&5
++$as_echo "$as_me: WARNING: cross compiling: not checking" >&2;}
++ krb5threadsafe="-DKRB5_IS_THREAD_SAFE"
+ else
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
- #include <krb5.h>
-@@ -4028,7 +4022,6 @@ $as_echo "$as_me: WARNING: libkrb5 is no
- fi
- rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
- conftest.$ac_objext conftest.beam conftest.$ac_ext
--fi
-
+--- a/src/modules/rlm_krb5/configure.ac
++++ b/src/modules/rlm_krb5/configure.ac
+@@ -143,7 +143,9 @@ if test "$krb5threadsafe" != "no"; then
+ FR_SMART_CHECK_LIB(krb5, krb5_is_thread_safe)
+ if test "x$ac_cv_lib_krb5_krb5_is_thread_safe" = xyes; then
+ AC_RUN_IFELSE([AC_LANG_PROGRAM([[#include <krb5.h>]], [[return krb5_is_thread_safe() ? 0 : 1]])],
+- [krb5threadsafe="-DKRB5_IS_THREAD_SAFE"], [AC_MSG_WARN([[libkrb5 is not threadsafe]])])
++ [krb5threadsafe="-DKRB5_IS_THREAD_SAFE"], [AC_MSG_WARN([[libkrb5 is not threadsafe]])],
++ [AC_MSG_WARN(cross compiling: not checking)
++ krb5threadsafe="-DKRB5_IS_THREAD_SAFE"])
fi
else
+ krb5threadsafe=""
--- /dev/null
+#!/bin/sh
+
+FR_ETC="/etc/freeradius3"
+FR_LIB="/usr/lib/freeradius3"
+FR_SHARE="/usr/share/freeradius3"
+
+case "$1" in
+freeradius3)
+ [ -x /usr/sbin/radiusd ] || { echo "FAIL: /usr/sbin/radiusd not installed"; exit 1; }
+
+ for f in radiusd.conf clients.conf proxy.conf \
+ policy.d/accounting policy.d/filter \
+ sites-available/default sites-enabled/default; do
+ [ -s "$FR_ETC/$f" ] || { echo "FAIL: $FR_ETC/$f missing or empty"; exit 1; }
+ done
+
+ # radiusd derives every other config path from raddbdir.
+ grep -q "^raddbdir = $FR_ETC\$" "$FR_ETC/radiusd.conf" || {
+ echo "FAIL: radiusd.conf does not set raddbdir to $FR_ETC"
+ grep -n '^raddbdir' "$FR_ETC/radiusd.conf"
+ exit 1
+ }
+
+ [ -x /etc/init.d/radiusd ] || { echo "FAIL: /etc/init.d/radiusd not installed"; exit 1; }
+ ;;
+
+freeradius3-default)
+ # Pulls in the modules radiusd.conf expects in mods-enabled/, so this is
+ # the first package where the server can start. -XC reads the whole
+ # config, loads every module and initialises OpenSSL, then exits.
+ radiusd -XC || {
+ echo "FAIL: 'radiusd -XC' could not start the server"
+ exit 1
+ }
+ ;;
+
+freeradius3-common)
+ for l in dhcp eap radius server; do
+ [ -s "$FR_LIB/libfreeradius-$l.so" ] || {
+ echo "FAIL: $FR_LIB/libfreeradius-$l.so not installed"; exit 1; }
+ done
+
+ [ -s "$FR_ETC/dictionary" ] || { echo "FAIL: $FR_ETC/dictionary missing or empty"; exit 1; }
+ [ -s "$FR_SHARE/dictionary" ] || { echo "FAIL: $FR_SHARE/dictionary missing or empty"; exit 1; }
+
+ # The Makefile packages only PKG_DICTIONARIES; a still-active $INCLUDE
+ # without a file behind it stops radiusd from starting.
+ includes=$(sed -n 's/^\$INCLUDE[[:space:]][[:space:]]*\(dictionary\.[^[:space:]]*\).*/\1/p' \
+ "$FR_SHARE/dictionary")
+ [ -n "$includes" ] || {
+ echo "FAIL: no active \$INCLUDE lines in $FR_SHARE/dictionary"; exit 1; }
+
+ missing=
+ for d in $includes; do
+ [ -f "$FR_SHARE/$d" ] || missing="$missing $d"
+ done
+ [ -z "$missing" ] || {
+ echo "FAIL: \$INCLUDE with no dictionary file behind it:$missing"; exit 1; }
+ ;;
+
+freeradius3-utils)
+ for t in radclient radeapclient radwho; do
+ [ -x "/usr/bin/$t" ] || { echo "FAIL: /usr/bin/$t not installed"; exit 1; }
+ "/usr/bin/$t" -h 2>&1 | grep -q "Usage: $t" || {
+ echo "FAIL: '$t -h' did not print its usage"
+ "/usr/bin/$t" -h 2>&1 | head -n 5
+ exit 1
+ }
+ done
+
+ [ -x /usr/bin/radtest ] || { echo "FAIL: /usr/bin/radtest not installed"; exit 1; }
+ radtest 2>&1 | grep -q "Usage: radtest" || {
+ echo "FAIL: 'radtest' did not print its usage"
+ radtest 2>&1 | head -n 5
+ exit 1
+ }
+
+ # Use a TEST-NET HOSTNAME with no NAS name to verify patch 004 passes it
+ # as NAS-IP-Address; capture xtrace and stop radclient with no server.
+ nas_ip="192.0.2.77"
+ radtest_out="/tmp/radtest-hostname.$$"
+ HOSTNAME="$nas_ip" sh -x /usr/bin/radtest u p 127.0.0.1:1812 0 testing123 \
+ >"$radtest_out" 2>&1 &
+ radtest_pid=$!
+ sleep 3
+ kill "$radtest_pid" 2>/dev/null
+ killall radclient 2>/dev/null
+ grep -q "NAS-IP-Address = $nas_ip" "$radtest_out" || {
+ echo "FAIL: radtest did not use \$HOSTNAME ($nas_ip) for NAS-IP-Address"
+ grep -E 'nas=|NAS-IP-Address' "$radtest_out" | head -n 10
+ rm -f "$radtest_out"
+ exit 1
+ }
+ rm -f "$radtest_out"
+ ;;
+
+freeradius3-democerts)
+ for f in ca.pem server.pem; do
+ [ -s "$FR_ETC/certs/$f" ] || { echo "FAIL: $FR_ETC/certs/$f missing or empty"; exit 1; }
+ grep -q "BEGIN CERTIFICATE" "$FR_ETC/certs/$f" || {
+ echo "FAIL: $FR_ETC/certs/$f holds no certificate"; exit 1; }
+ done
+
+ grep -q "BEGIN .*PRIVATE KEY" "$FR_ETC/certs/server.pem" || {
+ echo "FAIL: $FR_ETC/certs/server.pem holds no private key"; exit 1; }
+ ;;
+esac
+
+exit 0