radius: send the session ID as Acct-Session-Id in the Access-Request

RFC 2866 (5.5) allows an Access-Request to carry Acct-Session-Id and
requires the same value in the session's Accounting-Requests. Sending it
already at authentication time lets the RADIUS server correlate the two
exchanges by a single per-session key (e.g. for rlm_ippool, so concurrent
sessions of the same user from the same client do not collide on one IP
lease).

Documented as REQ-AUTH-AUTH-025, with a positive check in tests/radius
that the id sent as Acct-Session-Id in the Access-Request matches the
Accounting-Request.

Signed-off-by: Dmitrii <dimmispencer@gmail.com>
Resolves: #751
This commit is contained in:
Dmitrii
2026-07-14 21:40:23 +02:00
committed by Nikos Mavrogiannopoulos
parent 6d4f96aa72
commit df086188d7
7 changed files with 73 additions and 3 deletions
+1
View File
@@ -1,4 +1,5 @@
* Version 1.5.1 (unreleased) * Version 1.5.1 (unreleased)
- Send the RADIUS session ID as Acct-Session-Id in the Access-Request (#751)
- Hardened per-user/group configuration file lookup against path-traversal; - Hardened per-user/group configuration file lookup against path-traversal;
a malformed per-user/group or default configuration file now causes the a malformed per-user/group or default configuration file now causes the
affected session to be rejected rather than silently ignored. A configured affected session to be rejected rather than silently ignored. A configured
+16 -3
View File
@@ -734,18 +734,31 @@ containing: `User-Name` (REQ-AUTH-AUTH-024), `User-Password` (the submitted
password), `NAS-IP-Address` or `NAS-IPv6-Address` (from `e->our_ip`, whichever password), `NAS-IP-Address` or `NAS-IPv6-Address` (from `e->our_ip`, whichever
family applies), `NAS-Identifier` if `nas-identifier=` is configured family applies), `NAS-Identifier` if `nas-identifier=` is configured
(REQ-AUTH-AUTH-022), `Calling-Station-Id` (the client's `remote_ip`), (REQ-AUTH-AUTH-022), `Calling-Station-Id` (the client's `remote_ip`),
`Connect-Info` (the client's User-Agent string), `Service-Type = `Connect-Info` (the client's User-Agent string), `Acct-Session-Id` (the
session identifier, when available — see below), `Service-Type =
Authenticate-Only`, `NAS-Port-Type = Async`, and — when continuing a Authenticate-Only`, `NAS-Port-Type = Async`, and — when continuing a
challenge (REQ-AUTH-AUTH-027) — the `State` attribute echoed back from the challenge (REQ-AUTH-AUTH-027) — the `State` attribute echoed back from the
prior `Access-Challenge`. prior `Access-Challenge`.
The `Acct-Session-Id` carries the same session identifier
(`acct_info.safe_id`) that is later sent in the Accounting-Request packets
(REQ-AUTH-ACCT-*). RFC 2866 (section 5.5) allows an Access-Request to carry
`Acct-Session-Id` ("An Access-Request packet MAY have an Acct-Session-Id"),
and requires that if it does, the NAS MUST use the same value in the
Accounting-Request packets for that session — so the two exchanges correlate
on one key. Emitting it during authentication gives the RADIUS server a
per-session key already at Access-Request time (e.g. for `rlm_ippool`, so
concurrent sessions of the same user from the same client do not collide on a
single IP lease).
**Strength:** MUST **Strength:** MUST
**Status:** DERIVED **Status:** DERIVED
**Source:** src/auth/radius.c:293-430 (request construction, before **Source:** src/auth/radius.c:293-430 (request construction, before
`rc_auth()`/`rc_send_server()`) `rc_auth()`/`rc_send_server()`)
**Acceptance:** unit, local — capture the RADIUS request (e.g. via a test **Acceptance:** unit, local — capture the RADIUS request (e.g. via a test
FreeRADIUS server with `auth_log`) for a normal login; confirm all listed FreeRADIUS server with `auth_log`) for a normal login; confirm all listed
attributes are present with expected values, and that a configured attributes are present with expected values, that a configured
`nas-identifier` appears as `NAS-Identifier`. `nas-identifier` appears as `NAS-Identifier`, and that `Acct-Session-Id` is
present and equals the session id reported in the matching Accounting-Request.
**Links:** REQ-AUTH-AUTH-024, REQ-AUTH-AUTH-026, REQ-AUTH-AUTH-027 **Links:** REQ-AUTH-AUTH-024, REQ-AUTH-AUTH-026, REQ-AUTH-AUTH-027
### REQ-AUTH-AUTH-026 — `Access-Accept` attributes populate group membership and per-session network configuration ### REQ-AUTH-AUTH-026 — `Access-Accept` attributes populate group membership and per-session network configuration
+19
View File
@@ -167,6 +167,9 @@ static int radius_auth_init(void **ctx, void *pool, void *_vctx,
strlcpy(pctx->user_agent, info->user_agent, strlcpy(pctx->user_agent, info->user_agent,
sizeof(pctx->user_agent)); sizeof(pctx->user_agent));
if (info->sid)
strlcpy(pctx->sid, info->sid, sizeof(pctx->sid));
*ctx = pctx; *ctx = pctx;
return ERR_AUTH_CONTINUE; return ERR_AUTH_CONTINUE;
@@ -375,6 +378,22 @@ static int radius_auth_pass(void *ctx, const char *pass, unsigned int pass_len)
goto cleanup; goto cleanup;
} }
/* RFC 2866 (5.5) allows an Access-Request to carry Acct-Session-Id and
* requires the same value in the session's Accounting-Requests. Sending
* it already at authentication time lets the RADIUS server correlate the
* two exchanges by a single per-session key (#751). */
if (pctx->sid[0] != 0) {
if (rc_avpair_add(pctx->vctx->rh, &send, PW_ACCT_SESSION_ID,
pctx->sid, -1, 0) == NULL) {
oc_syslog(
LOG_ERR,
"%s:%u: error in constructing radius message for user '%s'",
__func__, __LINE__, pctx->username);
ret = ERR_AUTH_FAIL;
goto cleanup;
}
}
if (pctx->user_agent[0] != 0) { if (pctx->user_agent[0] != 0) {
if (rc_avpair_add(pctx->vctx->rh, &send, PW_CONNECT_INFO, if (rc_avpair_add(pctx->vctx->rh, &send, PW_CONNECT_INFO,
pctx->user_agent, -1, 0) == NULL) { pctx->user_agent, -1, 0) == NULL) {
+1
View File
@@ -41,6 +41,7 @@ struct radius_vhost_ctx {
struct radius_ctx_st { struct radius_ctx_st {
char username[MAX_USERNAME_SIZE * 2]; char username[MAX_USERNAME_SIZE * 2];
char user_agent[MAX_AGENT_NAME]; char user_agent[MAX_AGENT_NAME];
char sid[SAFE_ID_SIZE]; /* session id, sent as Acct-Session-Id */
char *groupnames[MAX_GROUPS]; char *groupnames[MAX_GROUPS];
unsigned int groupnames_size; unsigned int groupnames_size;
+1
View File
@@ -942,6 +942,7 @@ int handle_sec_auth_init(int cfd, sec_mod_st *sec, const SecAuthInitMsg *req,
st.ip = req->remote_ip; st.ip = req->remote_ip;
st.our_ip = req->our_ip; st.our_ip = req->our_ip;
st.user_agent = req->user_agent; st.user_agent = req->user_agent;
st.sid = e->acct_info.safe_id;
st.id = pid; st.id = pid;
ret = e->module->auth_init(&e->auth_ctx, e, e->vhost_auth_ctx, ret = e->module->auth_init(&e->auth_ctx, e, e->vhost_auth_ctx,
+1
View File
@@ -63,6 +63,7 @@ typedef struct common_auth_init_st {
const char *ip; const char *ip;
const char *our_ip; const char *our_ip;
const char *user_agent; const char *user_agent;
const char *sid; /* printable session id (== acct_info.safe_id) */
unsigned int id; unsigned int id;
} common_auth_init_st; } common_auth_init_st;
+34
View File
@@ -204,4 +204,38 @@ if test -z "$OCTETS" || test "$OCTETS" = 0;then
fi fi
echo "Transferred ${OCTETS} bytes" echo "Transferred ${OCTETS} bytes"
# REQ-AUTH-AUTH-025: the session id must be sent as Acct-Session-Id already in
# the Access-Request, using the same value that later appears in the
# Accounting-Request so the two exchanges correlate.
echo " * Verifying Acct-Session-Id is sent during authentication..."
# Session id reported by the accounting exchange of the connected session.
ACCT_SID=$(awk '
/Received Accounting-Request/ { in_acct=1 }
/Received Access-Request/ { in_acct=0 }
in_acct && /Acct-Session-Id/ {
v=$0; sub(/.*Acct-Session-Id = /, "", v); gsub(/"/, "", v); last=v
}
END { print last }' "${RADIUSLOG}")
if test -z "${ACCT_SID}";then
cat ${RADIUSLOG}
echo "No Acct-Session-Id present in the Accounting-Request!"
exit 1
fi
# The same identifier must have been present in an Access-Request.
FOUND=$(awk -v sid="${ACCT_SID}" '
/Received Access-Request/ { in_access=1 }
/Received Accounting-Request/ { in_access=0 }
in_access && /Acct-Session-Id/ && index($0, sid) { print "yes"; exit }
' "${RADIUSLOG}")
if test "${FOUND}" != "yes";then
cat ${RADIUSLOG}
echo "Acct-Session-Id ${ACCT_SID} was not sent in any Access-Request!"
exit 1
fi
echo "Acct-Session-Id ${ACCT_SID} present in both Access-Request and Accounting-Request"
exit 0 exit 0