Skip to content

uac_auth: parse Authorization nonce-count as hex in checkAuthentication#556

Open
hecko wants to merge 1 commit into
masterfrom
hecko/uac-auth-nonce-count-hex
Open

uac_auth: parse Authorization nonce-count as hex in checkAuthentication#556
hecko wants to merge 1 commit into
masterfrom
hecko/uac-auth-nonce-count-hex

Conversation

@hecko

@hecko hecko commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Bug

When SEMS validates an incoming request as a UAS, UACAuth::checkAuthentication (core/plug-in/uac_auth/UACAuth.cpp) reads the digest nc (nonce-count) attribute from the Authorization header and parses it with str2i, i.e. as decimal:

string nonce_count_str = find_attribute("nc", auth_hdr);
if (str2i(nonce_count_str, client_nonce_count)) {   // decimal parse
  DBG("Error parsing nonce_count '%s'\n", nonce_count_str.c_str());
  goto auth_end;
}
...
uac_calc_response(ha1, ha2, r_challenge, r_cnonce, qop_value, client_nonce_count, response);

Per RFC 2617 / RFC 7616 the nonce-count is an 8-digit hexadecimal value (00000001, 00000002, … 0000000a, …). uac_calc_response then re-formats the parsed integer back to hex via int2hex(nonce_count, true) to build the digest.

For nonce-counts below 0x0a the decimal and hex forms coincide, so the bug is invisible on the first few requests. But a client using qop=auth/auth-int that reuses a nonce hits nc=0000000a on its 10th request: str2i fails on the a and the request is rejected. Larger values such as nc=00000010 are decimal-parsed as 10 → re-formatted to 0000000a ≠ the client's 00000010, giving a digest mismatch. Either way, a correctly-authenticating client starts receiving spurious 401s once its nonce-count contains a hex digit af, breaking long-lived authenticated flows.

Fix

Feed the client's original nc string straight into the digest instead of re-parsing and re-formatting it:

  • uac_calc_response() now takes the nonce-count as a const string& rather than unsigned int.
  • The UAS path (checkAuthentication) passes the request's verbatim nc string.
  • The UAC path (do_auth) passes int2hex(nonce_count, true), preserving its previous behaviour exactly.

This makes the recomputed response match the client's regardless of the nc magnitude, and also makes the comparison exact with respect to the client's formatting.

Validation

  • Confirmed on current master that checkAuthentication parses nc with str2i and that uac_calc_response internally applied int2hex(nonce_count, true).
  • Both call sites (do_auth, checkAuthentication) and the declaration in UACAuth.h are updated; git grep shows no other callers.
  • g++ -fsyntax-only -std=c++17 on the translation unit passes. No behavioural change for the client (UAC) path or for nc values ≤ 0x09.

Acknowledgement

Backport of the intent of the yeti-switch/sems fixes, commits a563fd5b ("UACAuth: use hex2int to parse nonce_count") and af6a7a16 ("Fixed nonce count processing case-sensivity"). This tree lacks yeti's hex2int helper, so the string-passthrough form is used instead. Thanks to the yeti-switch maintainers.


Generated by Claude Code

UACAuth::checkAuthentication() reads the digest "nc" (nonce-count)
attribute from an incoming Authorization header and parses it with
str2i(), which interprets the value as decimal. Per RFC 2617/7616 the
nonce-count is an 8-digit hexadecimal number ("00000001", "0000000a",
...). SEMS then recomputes the expected response digest from that value
via int2hex(nonce_count, true).

For nonce-counts below 0x0a the two representations agree, but once a
client using qop=auth/auth-int reaches its 10th request under one nonce
it sends nc=0000000a: str2i() fails on the 'a' and the request is
rejected; higher values such as nc=00000010 (decimal-parsed as 10 =>
"0000000a") produce a digest mismatch. Either way a correctly
authenticating client starts getting spurious 401s once its nonce-count
contains a hex digit a-f, breaking long-lived authenticated flows.

Feed the client's original nc string straight into the digest instead of
re-parsing and re-formatting it: uac_calc_response() now takes the
nonce-count as a string; the UAS path passes the request's verbatim nc
and the UAC (do_auth) path passes int2hex(nonce_count, true). This also
makes the response comparison exact with respect to the client's
formatting.

Mirrors yeti-switch/sems commits a563fd5b and af6a7a16.

Author: @hecko
Copilot AI review requested due to automatic review settings July 12, 2026 03:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants