Skip to content

Follow up for PR 1218 - #1239

Open
aidankeefe2022 wants to merge 2 commits into
masterfrom
follow-up-for-PR-1218
Open

Follow up for PR 1218#1239
aidankeefe2022 wants to merge 2 commits into
masterfrom
follow-up-for-PR-1218

Conversation

@aidankeefe2022

Copy link
Copy Markdown
Member

Added more tests, broke up ValidateProtoId conditions to make better errors and added more checks, added ProtoIdSz field to travel with the ProtoId buffer.

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fenrir Automated Review — PR #1239

Scan targets checked: wolfssh-bugs, wolfssh-src

Findings: 1
1 finding(s) posted as inline comments (see file-level comments below)

This review was generated automatically by Fenrir. Reported findings require changes before merge.

Comment thread tests/unit.c
Comment thread tests/unit.c
wolfSSH_CTX_SetSshProtoIdStr() now rejects a string that is not
CRLF-terminated, exceeds 255 bytes counting the terminator, or
carries a CR or LF in the body. DoKexInit() subtracts the two
terminator bytes from the length when hashing it, so an
unterminated string underflowed the hash length.

Issue: F-10571

partial progress

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fenrir Automated Review — PR #1239

Scan targets checked: wolfssh-bugs, wolfssh-src

Fenrir result: Approved ✅

No new issues found in the changed files.

Advisory only — this automated result does not count as a GitHub approval.

@wolfSSL-Fenrir-bot
wolfSSL-Fenrir-bot dismissed their stale review September 3, 2026 19:32

Fenrir's latest completed scan found no issues; clearing the prior automated change request.

@ejohnstown ejohnstown 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.

Went through the Skoll report on this PR and checked each finding against the code. Built the branch with --enable-all under -Wall -Werror: clean, and tests/unit.test passes (DoProtoId: SUCCESS, binary exits 0). No correctness defects -- everything below is a test or naming point.

Suggested order: the rename first, then the single consistency assertion in the loop. The fixture indices and the KEX-coverage note are optional.

One thing not in the report, about the ssh.h change: caching the size is the only semantic change here. An embedder who rewrites the referenced buffer in place to something shorter now gets a stale length in SendProtoId()'s GrowBuffer/WMEMCPY, where before WSTRLEN() was recomputed at send time and stayed self-consistent. The new doc line ("valid and unmodified ... a later in-place rewrite of the buffer is not revalidated") covers exactly that -- worth knowing that line is load-bearing, not decoration.

Comment thread tests/unit.c Outdated
if ((ret == WS_SUCCESS) && clientCtx->sshProtoIdStrSz !=
WSTRLEN(protoIds[i].id)) {
fprintf(stderr,
"\t[protoId %d] \"%s\" FAIL: stored sshProtoIdSz "

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.

The field this PR adds is sshProtoIdStrSz (see wolfssh/internal.h), but this message prints sshProtoIdSz. A grep for the name a failing test prints should land on the field it is talking about. The PR description has the same slip.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

fixed

Comment thread tests/unit.c Outdated
Comment on lines +607 to +614
if ((ret == WS_SUCCESS) && clientCtx->sshProtoIdStrSz !=
WSTRLEN(protoIds[i].id)) {
fprintf(stderr,
"\t[protoId %d] \"%s\" FAIL: stored sshProtoIdSz "
"was not retained\n",
i, protoIds[i].name);
failures++;
}

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.

This checks the size only on the success path. A future edit that assigns ctx->sshProtoIdStrSz before ValidateProtoId() runs, or that clears one field without the other on rejection, would leave the CTX with a size that no longer matches the string, and this test would still pass. That desync is exactly what DoKexInit() can no longer catch now that it does not recompute WSTRLEN().

Cheapest fix is one unconditional assertion after each call, outside the ret == WS_SUCCESS guard:

if (clientCtx->sshProtoIdStrSz != (word32)WSTRLEN(clientCtx->sshProtoIdStr)) {
    /* fields are out of sync */
}

That proves the pair is consistent regardless of outcome, and the table already sets it up: "exact len custom ID" succeeds and every vector after it is expected to fail, so the tail is a ready-made "a reject must not clobber the stored ID" sequence. Safe to add -- this block is the last use of clientCtx before the frees.

@aidankeefe2022 aidankeefe2022 Sep 4, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

added check that a failing proto id does not write to the ctx struct and added the size compare check

Comment thread tests/unit.c Outdated
Comment on lines +586 to +595
WMEMSET(tooLongProtoId, 'a', sizeof(tooLongProtoId));
WMEMCPY(tooLongProtoId, "SSH-2.0-", sizeof("SSH-2.0-") - 1);
tooLongProtoId[256] = '\0';
tooLongProtoId[255] = '\n';
tooLongProtoId[254] = '\r';
WMEMSET(justRightProtoId, 'a', sizeof(justRightProtoId));
WMEMCPY(justRightProtoId, "SSH-2.0-", sizeof("SSH-2.0-") - 1);
justRightProtoId[255] = '\0';
justRightProtoId[254] = '\n';
justRightProtoId[253] = '\r';

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.

These literal indices restate the [257]/[256] declarations above and WOLFSSH_PROTOID_LIMIT; change any one of the three and the terminator lands in the wrong place. Because the arrays are WMEMSET to 'a', a misplaced NUL still yields a well-formed-looking C string, so the test would keep passing while no longer sitting on the boundary it is meant to test.

WOLFSSH_PROTOID_LIMIT is already visible here through wolfssh/internal.h, so declaring tooLongProtoId[WOLFSSH_PROTOID_LIMIT + 2] and justRightProtoId[WOLFSSH_PROTOID_LIMIT + 1], then writing the terminators at sizeof(buf) - 1 / - 2 / - 3, makes "exactly at the limit" and "one past it" self-evident and self-maintaining.

Comment thread src/internal.c

strSz = (word32)WSTRLEN(ssh->ctx->sshProtoIdStr) - SSH_PROTO_EOL_SZ;
/* The ID is hashed without its terminator. */
strSz = ssh->ctx->sshProtoIdStrSz - SSH_PROTO_EOL_SZ;

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.

Nothing in the tree drives a handshake with a custom proto ID. wolfSSH_CTX_SetSshProtoIdStr() has exactly one caller, the unit test, and that only checks the accept/reject verdict and the stored size. So the one path this field exists for has no KEX coverage, and a desync between sshProtoIdStr and sshProtoIdStrSz would surface only as an exchange-hash mismatch, which is a miserable failure to diagnose.

No live defect -- CtxInit() and the setter both assign the string and the size from the same source in the same breath -- so this is optional rather than owed. If it does get written, the max-length case is the interesting one, and it should pass: a 255-byte ID is 253 body bytes plus CRLF, and the peer's GetInputLine() reads all 255 in one go and finds the LF before the length >= WOLFSSH_PROTOID_LIMIT cap fires. The accept boundary this PR adds does round-trip.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Created round trip test!

Comment thread src/internal.c
}

for (i = 0; i < len - SSH_PROTO_EOL_SZ; i++) {
byte c = (byte)protoIdStr[i];

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.

For the record, no change needed. RFC 4253 Section 4.2 has softwareversion as printable US-ASCII excluding whitespace and the minus sign, so accepting all of 0x20-0x7e is looser than the spec -- but it is a tightening over the old check, and the mandatory SSH-2.0- prefix contains -, so a strict split would have to exempt the prefix. It also cannot affect interop: ValidateProtoId() only ever sees the locally configured string, and a peer's ID goes through DoProtoId() untouched by this change.

If anything, a one-line comment noting that SP is permitted on purpose (it separates the RFC 4253 comments field) would keep a later reader from taking the leniency for an oversight.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Added comment

@wolfSSL-Fenrir-bot wolfSSL-Fenrir-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Fenrir Automated Review — PR #1239

Scan targets checked: wolfssh-bugs, wolfssh-src

Fenrir result: Approved ✅

No new issues found in the changed files.

Advisory only — this automated result does not count as a GitHub approval.

Added more test vectors as well as locking in behavior of VerifyProtoId
Added round trip test for proto id
Comment fixes
New define for proto id min size
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.

4 participants