Add HostKey support and other config file code improvements.

- hostKeyFile and authKeysFile members of the config are now dynamically
allocated strings owned by the config.
- Break out sshd_config option handling into more modular functions.
pull/435/head
Hayden Roche 2022-07-26 14:07:25 -07:00
parent 8d1d2fb633
commit 477fb6c5c8
2 changed files with 243 additions and 113 deletions

View File

@ -46,7 +46,7 @@ struct WOLFSSHD_CONFIG {
char* banner;
char* chrootDir;
char* ciphers;
char* hostKey;
char* hostKeyFile;
char* hostKeyAlgos;
char* kekAlgos;
char* listenAddress;
@ -182,7 +182,12 @@ void wolfSSHD_FreeConfig(WOLFSSHD_CONFIG* conf)
if (conf != NULL) {
heap = conf->heap;
wolfSSHD_FreeString(&conf->authKeysFile, heap);
if (conf->authKeysFile != NULL) {
wolfSSHD_FreeString(&conf->authKeysFile, heap);
}
if (conf->hostKeyFile != NULL) {
wolfSSHD_FreeString(&conf->hostKeyFile, heap);
}
WFREE(conf, heap, DYNTYPE_SSHD);
}
@ -190,133 +195,222 @@ void wolfSSHD_FreeConfig(WOLFSSHD_CONFIG* conf)
#define MAX_LINE_SIZE 160
/* returns WS_SUCCESS on success
* Fails if any option is found that is unknown/unsupported
*/
static int wolfSSHD_ParseConfigLine(WOLFSSHD_CONFIG* conf, const char* l,
int lSz)
typedef struct {
int tag;
const char* name;
} CONFIG_OPTION;
enum {
OPT_AUTH_KEYS_FILE = 0,
OPT_PRIV_SEP = 1,
OPT_PERMIT_EMPTY_PW = 2,
OPT_SUBSYSTEM = 3,
OPT_CHALLENGE_RESPONSE_AUTH = 4,
OPT_USE_PAM = 5,
OPT_X11_FORWARDING = 6,
OPT_PRINT_MOTD = 7,
OPT_ACCEPT_ENV = 8,
OPT_PROTOCOL = 9,
OPT_LOGIN_GRACE_TIME = 10,
OPT_HOST_KEY = 11
};
enum {
NUM_OPTIONS = 12
};
static const CONFIG_OPTION options[NUM_OPTIONS] = {
{OPT_AUTH_KEYS_FILE, "AuthorizedKeysFile"},
{OPT_PRIV_SEP, "UsePrivilegeSeparation"},
{OPT_PERMIT_EMPTY_PW, "PermitEmptyPasswords"},
{OPT_SUBSYSTEM, "Subsystem"},
{OPT_CHALLENGE_RESPONSE_AUTH, "ChallengeResponseAuthentication"},
{OPT_USE_PAM, "UsePAM"},
{OPT_X11_FORWARDING, "X11Forwarding"},
{OPT_PRINT_MOTD, "PrintMotd"},
{OPT_ACCEPT_ENV, "AcceptEnv"},
{OPT_PROTOCOL, "Protocol"},
{OPT_LOGIN_GRACE_TIME, "LoginGraceTime"},
{OPT_HOST_KEY, "HostKey"}
};
static int wolfSSHD_HandlePrivSep(WOLFSSHD_CONFIG* conf, const char* value)
{
int ret = WS_BAD_ARGUMENT;
int sz;
char* tmp;
int ret = WS_SUCCESS;
/* supported config options */
const char authKeyFile[] = "AuthorizedKeysFile";
const char privilegeSeparation[] = "UsePrivilegeSeparation";
const char loginGraceTime[] = "LoginGraceTime";
const char permitEmptyPasswords[] = "PermitEmptyPasswords";
sz = (int)XSTRLEN(authKeyFile);
if (lSz > sz && XSTRNCMP(l, authKeyFile, sz) == 0) {
ret = wolfSSHD_CreateString(&tmp, l + sz, lSz - sz, conf->heap);
if (ret == WS_SUCCESS) {
wolfSSHD_SetAuthKeysFile(conf, tmp);
}
if (conf == NULL || value == NULL) {
ret = WS_BAD_ARGUMENT;
}
sz = (int)XSTRLEN(privilegeSeparation);
if (lSz > sz && XSTRNCMP(l, privilegeSeparation, sz) == 0) {
char* privType = NULL;
ret = wolfSSHD_CreateString(&privType, l + sz, lSz - sz, conf->heap);
/* check if is an allowed option */
if (XSTRNCMP(privType, "sandbox", 7) == 0) {
if (ret == WS_SUCCESS) {
if (WSTRCMP(value, "sandbox") == 0) {
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Sandbox privilege separation");
ret = WS_SUCCESS;
}
if (XSTRNCMP(privType, "yes", 3) == 0) {
if (WSTRCMP(value, "yes") == 0) {
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Privilege separation enabled");
ret = WS_SUCCESS;
}
if (XSTRNCMP(privType, "no", 2) == 0) {
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Turning off privilege separation!");
ret = WS_SUCCESS;
if (WSTRCMP(value, "no") == 0) {
wolfSSH_Log(WS_LOG_INFO,
"[SSHD] Turning off privilege separation!");
}
if (ret != WS_SUCCESS) {
wolfSSH_Log(WS_LOG_ERROR,
"[SSHD] Unknown/supported privilege separation!");
"[SSHD] Unknown/supported privilege separation!");
ret = WS_BAD_ARGUMENT;
}
wolfSSHD_FreeString(&privType, conf->heap);
}
if (XSTRNCMP(l, "Subsystem", 9) == 0) {
//@TODO
ret = WS_SUCCESS;
return ret;
}
static int wolfSSHD_HandleLoginGraceTime(WOLFSSHD_CONFIG* conf,
const char* value)
{
int ret = WS_SUCCESS;
long num;
if (conf == NULL || value == NULL) {
ret = WS_BAD_ARGUMENT;
}
if (XSTRNCMP(l, "ChallengeResponseAuthentication", 31) == 0) {
//@TODO
ret = WS_SUCCESS;
}
if (XSTRNCMP(l, "UsePAM", 6) == 0) {
//@TODO
ret = WS_SUCCESS;
}
if (XSTRNCMP(l, "X11Forwarding", 13) == 0) {
//@TODO
ret = WS_SUCCESS;
}
if (XSTRNCMP(l, "PrintMotd", 9) == 0) {
//@TODO
ret = WS_SUCCESS;
}
if (XSTRNCMP(l, "AcceptEnv", 9) == 0) {
//@TODO
ret = WS_SUCCESS;
}
if (XSTRNCMP(l, "Protocol", 8) == 0) {
//@TODO
ret = WS_SUCCESS;
}
sz = (int)XSTRLEN(loginGraceTime);
if (lSz > sz && XSTRNCMP(l, loginGraceTime, sz) == 0) {
long num;
num = wolfSSHD_GetConfigInt(l + sz, lSz - sz, 1, conf->heap);
if (ret == WS_SUCCESS) {
num = wolfSSHD_GetConfigInt(value, XSTRLEN(value), 1, conf->heap);
if (num < 0) {
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue getting login grace time");
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue getting login grace "
"time");
ret = num;
}
else {
ret = WS_SUCCESS;
conf->loginTimer = num;
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Setting login grace time to %ld",
num);
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Setting login grace time to "
"%ld", num);
}
}
return ret;
}
sz = (int)XSTRLEN(permitEmptyPasswords);
if (lSz > sz && XSTRNCMP(l, permitEmptyPasswords, sz) == 0) {
char* emptyPasswd = NULL;
ret = wolfSSHD_CreateString(&emptyPasswd, l + sz, lSz - sz, conf->heap);
/* TODO: Can value be const? */
static int wolfSSHD_HandleConfigOption(WOLFSSHD_CONFIG* conf, int opt,
const char* value)
{
int ret = WS_BAD_ARGUMENT;
if (XSTRNCMP(emptyPasswd, "yes", 3) == 0) {
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Empty password enabled");
conf->permitEmptyPasswords = 1;
switch (opt) {
case OPT_AUTH_KEYS_FILE:
ret = wolfSSHD_SetAuthKeysFile(conf, value);
break;
case OPT_PRIV_SEP:
ret = wolfSSHD_HandlePrivSep(conf, value);
break;
case OPT_SUBSYSTEM:
/* TODO */
ret = WS_SUCCESS;
}
/* default is no */
if (XSTRNCMP(emptyPasswd, "no", 2) == 0) {
break;
case OPT_CHALLENGE_RESPONSE_AUTH:
/* TODO */
ret = WS_SUCCESS;
break;
case OPT_USE_PAM:
/* TODO */
ret = WS_SUCCESS;
break;
case OPT_X11_FORWARDING:
/* TODO */
ret = WS_SUCCESS;
break;
case OPT_PRINT_MOTD:
/* TODO */
ret = WS_SUCCESS;
break;
case OPT_ACCEPT_ENV:
/* TODO */
ret = WS_SUCCESS;
break;
case OPT_PROTOCOL:
/* TODO */
ret = WS_SUCCESS;
break;
case OPT_LOGIN_GRACE_TIME:
ret = wolfSSHD_HandleLoginGraceTime(conf, value);
break;
case OPT_HOST_KEY:
ret = wolfSSHD_SetHostPrivateKey(conf, value);
break;
default:
break;
}
return ret;
}
static int CountWhitespace(const char* in, int inSz, byte inv)
{
int i = 0;
if (in != NULL) {
for (; i < inSz; ++i) {
if (inv) {
if (isspace(in[i])) {
break;
}
}
else {
if (!isspace(in[i])) {
break;
}
}
}
wolfSSHD_FreeString(&emptyPasswd, conf->heap);
}
if (ret == WS_BAD_ARGUMENT) {
printf("unknown / unsuported config line\n");
return i;
}
/* returns WS_SUCCESS on success
* Fails if any option is found that is unknown/unsupported
*/
static int wolfSSHD_ParseConfigLine(WOLFSSHD_CONFIG* conf, const char* l,
int lSz)
{
int ret = WS_BAD_ARGUMENT;
int sz;
char tmp[MAX_FILENAME_SZ];
int idx;
const CONFIG_OPTION* found = NULL;
for (idx = 0; idx < NUM_OPTIONS; ++idx) {
sz = (int)WSTRLEN(options[idx].name);
if (lSz >= sz && WSTRNCMP(l, options[idx].name, sz) == 0) {
found = &options[idx];
break;
}
}
if (found != NULL) {
/*
* Count leading and trailing whitespace. Use that information to cut
* out just the string itself when creating tmp.
*/
idx = sz;
idx += CountWhitespace(l + idx, lSz - sz, 0);
sz = CountWhitespace(l + idx, lSz - sz, 1);
if (sz >= MAX_FILENAME_SZ) {
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Filename too long.");
ret = WS_FATAL_ERROR;
}
else {
WMEMCPY(tmp, l + idx, sz);
tmp[sz] = 0;
ret = wolfSSHD_HandleConfigOption(conf, found->tag, tmp);
}
}
else {
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error parsing config line.");
ret = WS_FATAL_ERROR;
}
(void)lSz;
return ret;
}
@ -372,43 +466,81 @@ int wolfSSHD_LoadSSHD(WOLFSSHD_CONFIG* conf, const char* filename)
char* wolfSSHD_GetAuthKeysFile(WOLFSSHD_CONFIG* conf)
{
if (conf != NULL)
return conf->authKeysFile;
return NULL;
char* ret = NULL;
if (conf != NULL) {
ret = conf->authKeysFile;
}
return ret;
}
int wolfSSHD_SetAuthKeysFile(WOLFSSHD_CONFIG* conf, const char* file)
{
int ret = WS_SUCCESS;
if (conf == NULL) {
return WS_BAD_ARGUMENT;
ret = WS_BAD_ARGUMENT;
}
conf->authKeysFile = (char*)file;
if (ret == WS_SUCCESS) {
if (conf->authKeysFile != NULL) {
wolfSSHD_FreeString(&conf->authKeysFile, conf->heap);
conf->authKeysFile = NULL;
}
return WS_SUCCESS;
if (file != NULL) {
ret = wolfSSHD_CreateString(&conf->authKeysFile, file,
WSTRLEN(file), conf->heap);
}
}
return ret;
}
char* wolfSSHD_GetBanner(WOLFSSHD_CONFIG* conf)
{
if (conf != NULL)
return conf->banner;
return NULL;
char* ret = NULL;
if (conf != NULL) {
ret = conf->banner;
}
return ret;
}
char* wolfSSHD_GetHostPrivateKey(WOLFSSHD_CONFIG* conf)
{
if (conf != NULL)
return conf->hostKey;
return NULL;
char* ret = NULL;
if (conf != NULL) {
ret = conf->hostKeyFile;
}
return ret;
}
int wolfSSHD_SetHostPrivateKey(WOLFSSHD_CONFIG* conf, const char* hostKeyFile)
int wolfSSHD_SetHostPrivateKey(WOLFSSHD_CONFIG* conf, const char* file)
{
if (conf == NULL)
return WS_BAD_ARGUMENT;
int ret = WS_SUCCESS;
conf->hostKey = (char*)hostKeyFile;
return WS_SUCCESS;
if (conf == NULL) {
ret = WS_BAD_ARGUMENT;
}
if (ret == WS_SUCCESS) {
if (conf->hostKeyFile != NULL) {
wolfSSHD_FreeString(&conf->hostKeyFile, conf->heap);
conf->hostKeyFile = NULL;
}
if (file != NULL) {
ret = wolfSSHD_CreateString(&conf->hostKeyFile, file,
WSTRLEN(file), conf->heap);
}
}
return ret;
}
word16 wolfSSHD_GetPort(WOLFSSHD_CONFIG* conf)

View File

@ -159,8 +159,6 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx)
}
#endif
/* TODO: Currently expects a private key in DER format. Add logic to handle
* PEM, too. wc_PemToDer. */
/* Load in host private key */
if (ret == WS_SUCCESS) {