From 90769af1f83063da9bfb18102d4fc4dcb11dc0cc Mon Sep 17 00:00:00 2001 From: Hayden Roche Date: Thu, 28 Jul 2022 13:10:00 -0700 Subject: [PATCH] Add a test program, test_configuration.c, for testing sshd config. I wanted to be able to test ParseConfigLine directly, so I added some preprocessor logic to expose this function (i.e. make it non-static) when building test_configuration. I fixed a couple bugs discovered by this new testing. --- apps/wolfsshd/configuration.c | 25 ++-- apps/wolfsshd/configuration.h | 5 +- apps/wolfsshd/include.am | 11 +- apps/wolfsshd/test/test_configuration.c | 149 ++++++++++++++++++++++++ 4 files changed, 179 insertions(+), 11 deletions(-) create mode 100644 apps/wolfsshd/test/test_configuration.c diff --git a/apps/wolfsshd/configuration.c b/apps/wolfsshd/configuration.c index 3f553270..5d9a6770 100644 --- a/apps/wolfsshd/configuration.c +++ b/apps/wolfsshd/configuration.c @@ -26,6 +26,12 @@ /* functions for parsing out options from a config file and for handling loading * key/certs using the env. filesystem */ +#ifdef WOLFSSHD_UNIT_TEST +#define WOLFSSHD_STATIC +#else +#define WOLFSSHD_STATIC static +#endif + #include #include #include @@ -91,7 +97,10 @@ static long GetConfigInt(const char* in, int inSz, int isTime, void* heap) WMEMCPY(num, in, sz); num[sz] = '\0'; ret = atol(num); - if (ret > 0) { + if (ret == 0 && WSTRCMP(in, "0") != 0) { + ret = WS_BAD_ARGUMENT; + } + else if (ret > 0) { ret = ret * mult; } WFREE(num, heap, DYNTYPE_SSHD); @@ -228,17 +237,14 @@ static int HandlePrivSep(WOLFSSHD_CONFIG* conf, const char* value) if (WSTRCMP(value, "sandbox") == 0) { wolfSSH_Log(WS_LOG_INFO, "[SSHD] Sandbox privilege separation"); } - - if (WSTRCMP(value, "yes") == 0) { + else if (WSTRCMP(value, "yes") == 0) { wolfSSH_Log(WS_LOG_INFO, "[SSHD] Privilege separation enabled"); } - - if (WSTRCMP(value, "no") == 0) { + else if (WSTRCMP(value, "no") == 0) { wolfSSH_Log(WS_LOG_INFO, "[SSHD] Turning off privilege separation!"); } - - if (ret != WS_SUCCESS) { + else { wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Unknown/supported privilege separation!"); ret = WS_BAD_ARGUMENT; @@ -331,7 +337,7 @@ static int HandlePort(WOLFSSHD_CONFIG* conf, const char* value) if (ret == WS_SUCCESS) { portInt = XATOI(value); - if (portInt < 0) { + if (portInt <= 0) { wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Unable to parse port number: %s.", value); ret = WS_BAD_ARGUMENT; @@ -437,7 +443,8 @@ static int CountWhitespace(const char* in, int inSz, byte inv) /* returns WS_SUCCESS on success * Fails if any option is found that is unknown/unsupported */ -static int ParseConfigLine(WOLFSSHD_CONFIG* conf, const char* l, int lSz) +WOLFSSHD_STATIC int ParseConfigLine(WOLFSSHD_CONFIG* conf, const char* l, + int lSz) { int ret = WS_BAD_ARGUMENT; int sz; diff --git a/apps/wolfsshd/configuration.h b/apps/wolfsshd/configuration.h index 4389ec18..ad7c70f8 100644 --- a/apps/wolfsshd/configuration.h +++ b/apps/wolfsshd/configuration.h @@ -25,7 +25,6 @@ typedef struct WOLFSSHD_CONFIG WOLFSSHD_CONFIG; #include "auth.h" - #define WOLFSSHD_PRIV_SEPARAT 0 #define WOLFSSHD_PRIV_SANDBOX 1 #define WOLFSSHD_PRIV_OFF 2 @@ -44,5 +43,9 @@ byte wolfSSHD_ConfigGetPermitEmptyPw(const WOLFSSHD_CONFIG* conf); long wolfSSHD_ConfigGetGraceTime(const WOLFSSHD_CONFIG* conf); byte wolfSSHD_ConfigGetPwAuth(const WOLFSSHD_CONFIG* conf); +#ifdef WOLFSSHD_UNIT_TEST +int ParseConfigLine(WOLFSSHD_CONFIG* conf, const char* l, int lSz); +#endif + #endif /* WOLFSSHD_H */ diff --git a/apps/wolfsshd/include.am b/apps/wolfsshd/include.am index 94985981..cc20dee1 100644 --- a/apps/wolfsshd/include.am +++ b/apps/wolfsshd/include.am @@ -9,6 +9,15 @@ apps_wolfsshd_wolfsshd_SOURCES = apps/wolfsshd/wolfsshd.c \ apps_wolfsshd_wolfsshd_LDADD = src/libwolfssh.la apps_wolfsshd_wolfsshd_DEPENDENCIES = src/libwolfssh.la -DISTCLEANFILES+= apps/wolfsshd/.libs/wolfsshd +noinst_PROGRAMS += apps/wolfsshd/test/test_configuration +apps_wolfsshd_test_test_configuration_SOURCES = apps/wolfsshd/test/test_configuration.c \ + apps/wolfsshd/configuration.c \ + apps/wolfsshd/auth.c +apps_wolfsshd_test_test_configuration_LDADD = src/libwolfssh.la +apps_wolfsshd_test_test_configuration_DEPENDENCIES = src/libwolfssh.la +apps_wolfsshd_test_test_configuration_CPPFLAGS = -DWOLFSSH_SSHD -DWOLFSSHD_UNIT_TEST -Iapps/wolfsshd/ + +DISTCLEANFILES+= apps/wolfsshd/.libs/wolfsshd \ + apps/wolfsshd/test/.libs/test_configuration endif BUILD_SSHD diff --git a/apps/wolfsshd/test/test_configuration.c b/apps/wolfsshd/test/test_configuration.c new file mode 100644 index 00000000..91da4076 --- /dev/null +++ b/apps/wolfsshd/test/test_configuration.c @@ -0,0 +1,149 @@ +#include + +#include +#include + +static void Log(const char* fmt, ...) +{ + va_list vlist; + + va_start(vlist, fmt); + vfprintf(stderr, fmt, vlist); + va_end(vlist); +} + +typedef int (*TEST_FUNC)(void); +typedef struct { + const char *name; + TEST_FUNC func; +} TEST_CASE; + +#define TEST_DECL(func) { #func, func } + +#define TEST_CASE_CNT (int)(sizeof(testCases) / sizeof(*testCases)) + +static void TestSetup(const TEST_CASE* tc) +{ + Log("Running %s.\n", tc->name); +} + +static void TestCleanup(void) +{ +} + +static int RunTest(const TEST_CASE* tc) +{ + int ret; + + TestSetup(tc); + + ret = tc->func(); + if (ret != 0) { + Log("%s FAILED.\n", tc->name); + } + else { + Log("%s PASSED.\n", tc->name); + } + + TestCleanup(); + + return ret; +} + +typedef struct { + const char* desc; + const char* line; + int shouldFail; +} CONFIG_LINE_VECTOR; + +static int test_ParseConfigLine(void) +{ + int ret = WS_SUCCESS; + int i; + WOLFSSHD_CONFIG* conf; + + static CONFIG_LINE_VECTOR vectors[] = { + /* Port tests. */ + {"Valid port", "Port 22", 0}, + {"Port too big", "Port 65536", 1}, + {"Negative port", "Port -99", 1}, + {"Port NaN", "Port wolfsshd", 1}, + {"Port no value", "Port \n", 1}, + + /* Whitespace tests. */ + {"Extra leading whitespace", "Port 22", 0}, + {"Extra trailing whitespace", "Port 22 \n", 0}, + + /* Privilege separation tests. */ + {"Privilege separation yes", "UsePrivilegeSeparation yes", 0}, + {"Privilege separation no", "UsePrivilegeSeparation no", 0}, + {"Privilege separation sandbox", "UsePrivilegeSeparation sandbox", 0}, + {"Privilege separation invalid", "UsePrivilegeSeparation wolfsshd", 1}, + + /* Login grace time tests. */ + {"Valid login grace time seconds", "LoginGraceTime 60", 0}, + {"Valid login grace time minutes", "LoginGraceTime 1m", 0}, + {"Valid login grace time hours", "LoginGraceTime 1h", 0}, + {"Invalid login grace time", "LoginGraceTime wolfsshd", 1}, + + /* Permit empty password tests. */ + {"Permit empty password no", "PermitEmptyPasswords no", 0}, + {"Permit empty password yes", "PermitEmptyPasswords yes", 0}, + {"Permit empty password invalid", "PermitEmptyPasswords wolfsshd", 1}, + + /* Password auth tests. */ + {"Password auth no", "PasswordAuthentication no", 0}, + {"Password auth yes", "PasswordAuthentication yes", 0}, + {"Password auth invalid", "PasswordAuthentication wolfsshd", 1}, + }; + const int numVectors = (int)(sizeof(vectors) / sizeof(*vectors)); + + conf = wolfSSHD_NewConfig(NULL); + if (conf == NULL) { + ret = WS_MEMORY_E; + } + + if (ret == WS_SUCCESS) { + for (i = 0; i < numVectors; ++i) { + Log(" Testing scenario: %s.", vectors[i].desc); + + ret = ParseConfigLine(conf, vectors[i].line, + WSTRLEN(vectors[i].line)); + + if ((ret == WS_SUCCESS && !vectors[i].shouldFail) || + (ret != WS_SUCCESS && vectors[i].shouldFail)) { + Log(" PASSED.\n"); + ret = WS_SUCCESS; + } + else { + Log(" FAILED.\n"); + ret = WS_FATAL_ERROR; + break; + } + } + } + + return ret; +} + +const TEST_CASE testCases[] = { + TEST_DECL(test_ParseConfigLine) +}; + +int main(int argc, char** argv) +{ + int i; + int ret = WS_SUCCESS; + + (void)argc; + (void)argv; + + for (i = 0; i < TEST_CASE_CNT; ++i) { + ret = RunTest(&testCases[i]); + if (ret != WS_SUCCESS) { + break; + } + } + + return ret; +}