OAuth/OIDC token-validation gaps: nonce not enforced when absent, at_hash alg, inbound bearer not signature/aud/exp-validated + unverified email used for authz
Los mantenedores suelen responder en 1 día
@michaelrsweet ya está trabajando en esto.
Desde el 27/6/2026.
Evaluación
Este issue todavía no se ha evaluado.
Descripción
Describe the bug
While reviewing the new (2.5) OAuth/OpenID Connect support I noticed three token-validation gaps. These are hardening / spec-compliance issues (2.5 is unreleased, so filing here per SECURITY.md rather than as an advisory); their real-world impact depends on the configured Identity Provider (IdP), so I'm raising them for discussion rather than asserting a concrete bypass.
1. nonce not enforced when the id_token omits the claim — cups/oauth.c
// cups/oauth.c:1358-1364 (cupsOAuthGetTokens)
jwt = cupsJWTImportString(id_value, CUPS_JWS_FORMAT_COMPACT);
jnonce = cupsJWTGetClaimString(jwt, "nonce");
nonce = oauth_load_value(auth_uri, resource_uri, _CUPS_OTYPE_NONCE, false);
// Check nonce
if (!jwt || (jnonce && nonce && strcmp(jnonce, nonce)))
goto done;
When CUPS sent a nonce (it is persisted whenever the openid scope is requested), OIDC Core §3.1.3.7 requires the returned id_token to contain a matching nonce. Here, if the id_token omits the claim (jnonce == NULL), the jnonce && nonce && ... test short-circuits and the nonce check is skipped, so the token is accepted on signature alone. Suggested fix: if a nonce was sent (stored value exists), require jnonce != NULL && !strcmp(jnonce, nonce).
2. at_hash only validated for SHA-256-class id_tokens — cups/oauth.c:1377-1391
The access-token hash is always computed with sha2-256 and the code requires exactly 16 decoded at_hash bytes. For an id_token signed with an RS/ES/PS-384 or -512 algorithm the correct at_hash is 24/32 bytes, so a legitimate token is rejected (at_hash_bytes != 16 → goto done). The hash algorithm should follow the id_token's signing algorithm.
3. cupsd does not cryptographically validate inbound Bearer tokens; authorization uses the unverified email claim — scheduler/auth.c
In the Bearer branch (scheduler/auth.c:725-782), cupsd passes the token to cupsOAuthGetUserId(), which (when no cached identity exists) forwards it to the IdP userinfo_endpoint and trusts the JSON response (cups/oauth.c:1454-1518). On this inbound path there is no local verification of the token signature, iss, aud/azp, exp, or nbf (the existing cupsJWTHasValidSignature() / JWKS code is only used in the client-side token-exchange flow). Consequently a token minted by the same IdP for a different relying party can authenticate to CUPS (audience confusion), since CUPS never checks the token was issued for it.
Additionally, authorization matches the unverified email claim:
// scheduler/auth.c:1893
else if (!_cups_strcasecmp(username, name) || (con->email[0] && !_cups_strcasecmp(con->email, name)))
// scheduler/auth.c:1887,1920 -> cupsArrayFind(og->members, con->email)
email_verified is never consulted. If the configured IdP issues tokens with a user-settable/unverified email, a user could set email to a value listed in a policy's Require user ... or an OAuthGroup member file and gain that identity's access.
Suggested fixes: validate inbound tokens (signature via the IdP JWKS, plus iss/aud/exp) before trusting them, and require email_verified before using email for authorization decisions.
Expected behavior
Token and claim validation should follow the OIDC spec (enforce nonce when sent, algorithm-appropriate at_hash, and audience/expiry/email_verified checks).
System Information:
- OS and its version: Ubuntu 24.04 (x86_64)
- CUPS version: master / 2.5b1 (OAuth/OIDC support is 2.5-only)
Additional context
These are validation/hardening gaps rather than a demonstrated exploit; severity is IdP-dependent. Happy to split into separate issues if the maintainers prefer.
- Lenguaje dominante
- C
- Estrellas
- 1.8k
- Forks
- 353
- Merge medio
- 19 h 41 min
- PR fusionados (30 d)
- 18
Preparar el entorno
- Incluye un Dockerfile o un archivo de Docker Compose
- Sin plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de OpenPrinting/cups
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
OpenPrinting/cups#1795 ·
Los mantenedores suelen responder en 1 día
-
Don't use xgettext sort option (-s) when generating POT filePosiblemente ocupada @michaelrsweet la tomó hace 14 días. Abiertoinvestigating
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
OpenPrinting/cups#1721 · 4 comentarios · 1 asignado ·
Los mantenedores suelen responder en 1 día
-
Dificultad 3/5 1-2 días Aptitud para principiantes 20/100
OpenPrinting/cups#1802 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 3/5 1-2 días Aptitud para principiantes 35/100
OpenPrinting/cups#1796 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 4/5 3-5 días Aptitud para principiantes 48/100
OpenPrinting/cups#1786 ·
Los mantenedores suelen responder en 1 día
Todos los issues de OpenPrinting/cups
Issues similares
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
Los mantenedores suelen responder en 1 día
-
elf_loader: Xtensa ignores the addend of R_XTENSA_GLOB_DAT / R_XTENSA_JMP_SLOT (C++ vtables)Posiblemente ocupada @diplfranzhoepfinger la tomó hoy. Abiertoelf_loader
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
espressif/esp-iot-solution#813 ·
Los mantenedores suelen responder en 1 día
-
Status: Opened Type: Bug
Dificultad 1/5 Menos de una hora Aptitud para principiantes 85/100
Los mantenedores suelen responder en 1 día