Skip to content

v1.5.0

Choose a tag to compare

@github-actions github-actions released this 06 Oct 15:44
· 8 commits to main since this release

axil logo

  • Framed responses: Content-Length + Connection: close on every axil_respond(). Responses previously carried no body framing, so a client could only delimit the body by connection close -- over TLS that requires close_notify, which was never sent either. Strict clients (OpenSSL 3.5+, modern browsers) discarded such responses as truncated, dropping Set-Cookie with them. 1xx/204/304 stay length-free per RFC 9110, and 101 upgrade paths bypass axil_respond() untouched. Explicitly set lengths are respected, never duplicated.
  • Best-effort TLS close_notify on teardown. axil_ssl_drop() now runs up to two non-blocking SSL_shutdown() calls before SSL_free(), ensuring clean TLS connection termination.
  • Teardown is ungated: unauthenticated connection cleanup (SECURITY S5.4). axil_disconnect() no longer requires DF_AUTHENTICATED and axil_close() no longer requires DF_CONNECTED. Resolves socket and PTY leakage vulnerabilities where unauthenticated descriptors outlived their TCP connection.
  • Per-connection generation tracking: Generational tracking on connection descriptors to eliminate stale hook dispatch and race conditions during rapid reconnects.
  • Fixed 303 Redirect Connection Teardown: Fixed connection closing and cookie persistence issues on HTTP 303 See Other redirects.
  • Post-Chroot Module Loading: Improved path canonicalization and search mechanics for dynamic modules running post-chroot.
    kept as a regression: an AUTHENTICATED PTY connection killed abruptly must
    still leave no live child, no PTY master, and clean HTTP on recycled fds.
  • Per-connection generation: axil_generation(). struct descr grows a
    monotonic generation bumped on every accept, so fd-keyed module state can
    reject stale entries. axil-tty's mux_state and NAWS entry carry it and
    mux_get()/mux_wsz_get() refuse a mismatch — even a leaked entry is inert
    on a recycled descriptor, which closes the whole class rather than the one
    instance above.
  • DF_AUTH_AUTO (flag 4096): -A publishes an identity, it does not prove
    one.
    AXIL_AUTOAUTH authenticates every connection as the server's own
    account, so under -A a name comparison cannot tell an asserted identity
    from a published one. The flag is set only by AXIL_AUTOAUTH (so it cannot
    survive descriptor reuse), drop_priviledges() refuses to spawn a child
    under it — a child would otherwise run as the operator while the request
    proved nothing — and any downstream authorization that trusts REMOTE_USER
    must refuse a descriptor carrying it (SECURITY.md S5.8).
  • FD_VALID(fd) before any [FD_SETSIZE] table. One macro, checked
    before the first index rather than after the frame has already been touched,
    covering descr_map, io, frame_map and ws_flags; on Windows
    socket_t is the unsigned SOCKET, so >= 0 would be a tautology and
    INVALID_SOCKET (~0) is rejected by the < FD_SETSIZE half. Accept now
    spells the sentinel out (fd == INVALID_SOCKET || fd == 0) instead of the
    fd <= 0 test that never fires for an unsigned ~0, and every teardown path
    closes with axil_sock_close() (closesocket() on Winsock) instead of
    close() on a CRT fd.
  • Tests: test-auth, test.sh and test-ws.py gain the teardown,
    recycled-fd, DF_AUTH_AUTO and FD_VALID assertions; SECURITY.md records
    S5.4 and S5.5.

Verification

gpg --verify CHECKSUMS.asc
sha256sum -c CHECKSUMS