Skip to content

pki: Fail sshsig_sign when no signature was produced - #19

Open
tiandiwonder wants to merge 1 commit into
ClickHouse/libssh-0.12.0from
fix-sshsig-sign-reports-success-without-signature
Open

tiandiwonder wants to merge 1 commit into
ClickHouse/libssh-0.12.0from
fix-sshsig-sign-reports-success-without-signature

Conversation

@tiandiwonder

Copy link
Copy Markdown

sshsig_sign documents a two-valued contract:

 * @param signature  Pointer to store the allocated signature string in the
 *                   armored format. Must be freed with ssh_string_free_char()
 *
 * @return SSH_OK on success, SSH_ERROR on error

Two paths break it. Both are reached after rc has been set to SSH_OK by sshsig_prepare_data, and both jump to the shared cleanup label without resetting it, so the single return rc reports success while signature is still the NULL it was initialised to:

Path Taken when
the #else of #ifdef WITH_FIDO2 an SK key type on a build without FIDO2
sig == NULL the signing backend refused the key

A caller that follows the documented contract and only checks the return code then dereferences NULL.

How it was found

A consumer segfaulted while signing with a 1024-bit RSA key on an OpenSSL FIPS build. The modulus is below the FIPS-approved minimum, so pki_sign_data returned NULL and the second path reported success. The caller did exactly what the contract allows:

if (int rc = sshsig_sign(..., &signature); rc != SSH_OK)
    /* raise */;
return String(signature);   /* strlen(NULL) */

Fix

Set rc = SSH_ERROR on both paths before goto cleanup. This matches the rest of the file: ssh_pki_generate_key returns SSH_ERROR directly from its equivalent #else /* WITH_FIDO2 */ branch, and sshsig_verify assigns rc = SSH_ERROR before its own goto cleanup.

Test

torture_sshsig_sign_error_leaves_no_signature signs with the public part of the fixture RSA key. pki_sign_data rejects a key without private material at its !ssh_key_is_private(privkey) guard and returns NULL, which reaches the sig == NULL path, so the defect reproduces with no FIPS build and no FIDO2 hardware.

Verified both ways, built with -DUNIT_TESTING=ON -DWITH_FIDO2=OFF:

Source Result
with the fix 7 of 7 pass
with only src/pki.c reverted 6 pass, the new test fails with 0 != 0xffffffffffffffff

The SK branch is not covered: it needs a build with FIDO2 where pki_sk_do_sign is unavailable, and the suite has no fixture for that. It is fixed because it is the same defect, in the same function, on the same variable.

`sshsig_sign` documents that it returns `SSH_OK` on success, and that on success the `signature` out
parameter holds the allocated armored signature. Two paths break that contract. Both are reached after
`rc` has been set to `SSH_OK` by `sshsig_prepare_data`, and both jump to the shared `cleanup` label
without resetting it, so the single `return rc` reports success while `signature` is still the `NULL`
it was initialised to:

- the `#else` of `#ifdef WITH_FIDO2`, taken for an SK key type on a build without FIDO2
- `sig == NULL`, taken when the signing backend refused the key

A caller that follows the documented contract and only checks the return code then dereferences
`NULL`. This was found as a segfault in a consumer signing with a 1024-bit RSA key on an OpenSSL FIPS
build, where the modulus is below the approved minimum, so `pki_sign_data` returns `NULL` and the
second path reports success.

Set `rc = SSH_ERROR` on both before `goto cleanup`, which is how the rest of the file already behaves:
`ssh_pki_generate_key` returns `SSH_ERROR` directly from its equivalent `#else  /* WITH_FIDO2 */`
branch, and `sshsig_verify` assigns `rc = SSH_ERROR` before its own `goto cleanup`.

The test signs with the public part of the fixture RSA key. `pki_sign_data` rejects a key without
private material at its `!ssh_key_is_private(privkey)` guard and returns `NULL`, reaching the second
path, so the defect reproduces without a FIPS build or FIDO2 hardware. Reverting only the
`src/pki.c` change makes it fail with `0 != 0xffffffffffffffff`.

The SK branch is not covered: that needs a build with FIDO2 where `pki_sk_do_sign` is unavailable, and
the suite has no fixture for it.
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.

1 participant