Skip to content

quic: a client CN carrying control characters is accepted, where TCP refuses it #200

Description

@MDA2AV

The two stacks disagree about what counts as a usable identity, and QUIC is the permissive one.

TlsSession.ExtractCommonName refuses a CN containing control characters, and says why:

a CN carrying CR or LF forges log lines, and one carrying it into a header splits the response. PeerSubject escapes these on its way through X509_NAME_oneline, which left the accessor sold as the safe one as the only raw one. Refusing beats sanitising: a name that had to be rewritten to be safe is not the name that was presented.

The QUIC shim applies only three of those rules. iq_record_subject (src/protocols/ioxide.ngtcp2/native/ioxide_ngtcp2_shim.c) keeps the CN when it is non-empty, shorter than peer_cn, and free of an embedded NUL:

if (cn_len > 0 && (size_t)cn_len < sizeof(c->peer_cn) && memchr(cn, 0, (size_t)cn_len) == NULL) {
    memcpy(c->peer_cn, cn, (size_t)cn_len);

There is no control-character check at all. So the same client certificate produces an authorizable name on QUIC and none on TCP, and QuicEngineConnection.PeerCommonName — the value documented as the one to authorize on — can carry CR/LF into whatever the handler logs next.

Needs a trusted CA willing to sign such a CN, so this is hardening rather than a live hole, but it is a divergence in the direction where the newer stack is weaker. The fix belongs in the shim next to the NUL check.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingseverity:mediumPerf cliff / hardening gap

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions