Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 15 additions & 7 deletions apps/wolfssh/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ static int load_der_file(const char* filename, byte** out, word32* outSz)
return -1;
}

in = (byte*)WMALLOC(inSz, NULL, 0);
/* Ensure trailing null so buffer is a string, even without a newline */
in = (byte*)WMALLOC(inSz + 1, NULL, 0);
if (in == NULL) {
WFCLOSE(NULL, file);
return -1;
Expand All @@ -114,8 +115,10 @@ static int load_der_file(const char* filename, byte** out, word32* outSz)
in = 0;
inSz = 0;
}
else
else {
in[inSz] = 0;
ret = 0;
}

*out = in;
*outSz = (word32)inSz;
Expand Down Expand Up @@ -370,11 +373,9 @@ int ClientPublicKeyCheck(const byte* pubKey, word32 pubKeySz, void* ctx)
}

if (ret == 0) {
/* load_der_file() loads exactly what's in the file. Since it is
* NL terminated lines of known host data, and the last line ends
* in a NL, overwrite that with a nul to terminate the new string. */
knownHosts[sz - 1] = 0;

/* load_der_file() nul terminates one byte past the file contents, so
* the buffer is already a string whether or not the last line ends
* in a newline. */
encodedKey = (char*)WMALLOC(WOLFSSH_CLIENT_ENCKEY_SIZE_ESTIMATE
+ WOLFSSH_CLIENT_PUBKEYTYPE_SIZE_ESTIMATE
+ WOLFSSH_CLIENT_FINGERPRINT_SIZE_ESTIMATE, NULL, 0);
Expand Down Expand Up @@ -421,6 +422,13 @@ int ClientPublicKeyCheck(const byte* pubKey, word32 pubKeySz, void* ctx)
lineCount++;
line = WSTRSEP(&cursor, "\n");
if (line != NULL && *line) {
size_t lineSz = WSTRLEN(line);

/* Remove trailing CR if present for comparison below */
if (lineSz > 0 && line[lineSz - 1] == '\r') {
line[lineSz - 1] = 0;
}

name = WSTRSEP(&line, " ");
keyType = WSTRSEP(&line, " ");
key = WSTRSEP(&line, " ");
Expand Down
118 changes: 118 additions & 0 deletions tests/regress.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

#include <wolfssl/wolfcrypt/coding.h>
#include <wolfssh/port.h>
#include <wolfssh/ssh.h>
#include <wolfssh/internal.h>
Expand Down Expand Up @@ -5797,6 +5799,119 @@ static void TestAppendKeyToFile(void)
#endif /* WOLFSSH_TEST_INTERNAL */


#ifdef WOLFSSL_BASE64_ENCODE

static void WriteKnownHosts(const char* path, const char* contents)
{
WFILE* f = WBADFILE;
word32 sz = (word32)WSTRLEN(contents);

AssertIntEQ(WFOPEN(NULL, &f, path, "wb"), 0);
AssertTrue(f != WBADFILE);
AssertIntEQ((word32)WFWRITE(NULL, contents, 1, sz, f), sz);
AssertIntEQ(WFCLOSE(NULL, f), 0);
}


/* known_hosts is a text file and POSIX lets its last line end without a
* newline. The parser used to nul out the final byte of the file, which ate
* the last base64 character of the last entry and made that host read as
* unknown. Match the last entry with a trailing newline, without one, and
* with CRLF line endings. */
static void TestKnownHostsLastEntry(void)
{
/* string("ssh-rsa"), then a zero certificate count so the RFC 6187 parse
* declines this blob, then filler. Only the name and the base64 of the
* whole blob matter to the known_hosts search. */
static const byte pubKey[] = {
0x00, 0x00, 0x00, 0x07, 's', 's', 'h', '-', 'r', 's', 'a',
0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04
};
static const struct {
const char* sep;
const char* tail;
const char* label;
} cases[] = {
{ "\n", "\n", "trailing newline" },
{ "\n", "", "no trailing newline" },
{ "\r\n", "\r\n", "CRLF endings" },
};
char targetName[] = "last.example.com";
char homeDir[64];
char sshDir[80];
char hostsPath[112];
char encoded[64];
char contents[256];
char* savedHome = NULL;
const char* home;
word32 encodedSz = (word32)sizeof(encoded);
int savedStdin, devNull;
unsigned int i;

WSNPRINTF(homeDir, sizeof(homeDir), "wolfssh_kh_%d.tmp", (int)getpid());
WSNPRINTF(sshDir, sizeof(sshDir), "%s/.ssh", homeDir);
WSNPRINTF(hostsPath, sizeof(hostsPath), "%s/known_hosts", sshDir);

AssertIntEQ(Base64_Encode_NoNl(pubKey, (word32)sizeof(pubKey),
(byte*)encoded, &encodedSz), 0);
AssertTrue(encodedSz < sizeof(encoded));
encoded[encodedSz] = 0;

home = getenv("HOME");
if (home != NULL) {
savedHome = (char*)WMALLOC(WSTRLEN(home) + 1, NULL, 0);
AssertNotNull(savedHome);
WSTRCPY(savedHome, home);
}

/* Plain mkdir/rmdir rather than WMKDIR/WRMDIR: those only exist in
* builds that compile the SCP or SFTP file system layer. */
AssertIntEQ(mkdir(homeDir, 0700), 0);
AssertIntEQ(mkdir(sshDir, 0700), 0);
AssertIntEQ(setenv("HOME", homeDir, 1), 0);

/* A regression falls through to the "add it to known hosts?" prompt, so
* point stdin at EOF: the test then fails rather than waiting forever. */
savedStdin = dup(STDIN_FILENO);
devNull = open("/dev/null", O_RDONLY);
if (devNull >= 0) {
dup2(devNull, STDIN_FILENO);
}

for (i = 0; i < sizeof(cases)/sizeof(cases[0]); i++) {
/* An entry for a different host goes first, so the match lands on the
* last line, the one the terminator used to overwrite. */
WSNPRINTF(contents, sizeof(contents),
"other.example.com ssh-rsa AAAA%s%s ssh-rsa %s%s",
cases[i].sep, targetName, encoded, cases[i].tail);
WriteKnownHosts(hostsPath, contents);

printf(" known_hosts with %s.\n", cases[i].label);
AssertIntEQ(ClientPublicKeyCheck(pubKey, (word32)sizeof(pubKey),
targetName), 0);
}

if (devNull >= 0) {
dup2(savedStdin, STDIN_FILENO);
close(devNull);
}
close(savedStdin);

if (savedHome != NULL) {
AssertIntEQ(setenv("HOME", savedHome, 1), 0);
WFREE(savedHome, NULL, 0);
}
else {
unsetenv("HOME");
}

(void)remove(hostsPath);
(void)rmdir(sshDir);
(void)rmdir(homeDir);
}
#endif /* WOLFSSL_BASE64_ENCODE */


int main(int argc, char** argv)
{
WOLFSSH_CTX* ctx;
Expand Down Expand Up @@ -5828,6 +5943,9 @@ int main(int argc, char** argv)
TestClientParseDestination();
#ifdef WOLFSSH_TEST_INTERNAL
TestAppendKeyToFile();
#endif
#ifdef WOLFSSL_BASE64_ENCODE
TestKnownHostsLastEntry();
#endif
TestAuthMessageBlockedDuringKeying(ssh);
TestUserauthFailureDuringKeying(ssh);
Expand Down
Loading