Skip to content
Merged
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
97 changes: 74 additions & 23 deletions apps/wolfsshd/auth.c
Original file line number Diff line number Diff line change
Expand Up @@ -387,13 +387,13 @@ static int ExtractSalt(char* hash, char** salt, int saltSz)

#if defined(WOLFSSH_HAVE_LIBCRYPT) || defined(WOLFSSH_HAVE_LIBLOGIN)
#ifdef WOLFSSHD_UNIT_TEST
int CheckPasswordHashUnix(const char* input, char* stored)
int CheckPasswordHashUnix(const char* input, const char* stored)
#else
static int CheckPasswordHashUnix(const char* input, char* stored)
static int CheckPasswordHashUnix(const char* input, const char* stored)
#endif
{
int ret = WSSHD_AUTH_SUCCESS;
char* hashedInput;
char* hashedInput = NULL;
word32 hashedInputSz = 0, storedSz = 0;

if (input == NULL || stored == NULL) {
Expand Down Expand Up @@ -1760,42 +1760,75 @@ static int CheckPublicKeyWIN(const char* usr,
}
#endif /* _WIN32*/

/* return WOLFSSH_USERAUTH_SUCCESS on success */
static int DoCheckUser(const char* usr, WOLFSSHD_AUTH* auth)
/* Returns 1 if 'usr' is root-equivalent for PermitRootLogin (any uid 0
* account, or the literal name "root"; name-only on Windows). Shared by
* DoCheckUser and RequestAuthentication so all enforcement points agree. */
static int IsRootUser(const char* usr)
{
int ret = WOLFSSH_USERAUTH_SUCCESS;
int rc;
int isRoot = 0;
WOLFSSHD_CONFIG* usrConf;
#ifndef _WIN32
struct passwd* pwInfo;
#endif

wolfSSH_Log(WS_LOG_INFO, "[SSHD] Checking user name %s", usr);

#ifndef _WIN32
/* PermitRootLogin covers every uid 0 account (so an alias like "toor"
* cannot bypass it) and the literal name "root", so a transient getpwnam
* failure cannot skip the check for root. */
pwInfo = getpwnam(usr);
if ((pwInfo != NULL && pwInfo->pw_uid == 0) || XSTRCMP(usr, "root") == 0) {
isRoot = 1;
}
#else
/* No uid 0 on Windows and no logon token yet at this pre-auth stage, so
* fall back to the literal name; a token based Administrators membership
* check would belong after authentication. */
if (XSTRCMP(usr, "root") == 0) {
isRoot = 1;
}
#endif
return isRoot;
}

/* Returns 1 if root login is denied outright, i.e. PermitRootLogin no.
* Used by DoCheckUser. */
WOLFSSHD_STATIC int IsRootLoginDenied(int isRoot, WOLFSSHD_CONFIG* usrConf)
{
return (isRoot == 1 &&
wolfSSHD_ConfigGetPermitRoot(usrConf) == WOLFSSHD_PERMIT_ROOT_NO);
}

/* Returns 1 if root password authentication is blocked, i.e.
* PermitRootLogin prohibit-password or forced-commands-only. Used by
* RequestAuthentication for WOLFSSH_USERAUTH_PASSWORD. */
WOLFSSHD_STATIC int IsRootPasswordAuthBlocked(int isRoot,
WOLFSSHD_CONFIG* usrConf)
{
return (isRoot == 1 &&
(wolfSSHD_ConfigGetPermitRoot(usrConf) ==
WOLFSSHD_PERMIT_ROOT_PROHIBIT_PW ||
wolfSSHD_ConfigGetPermitRoot(usrConf) ==
WOLFSSHD_PERMIT_ROOT_FORCED_CMD));
}

/* Returns 1 if root public key login is missing the ForceCommand required by
* PermitRootLogin forced-commands-only. Used by RequestAuthentication for
* WOLFSSH_USERAUTH_PUBLICKEY. */
WOLFSSHD_STATIC int IsRootPubKeyForcedCmdMissing(int isRoot,
WOLFSSHD_CONFIG* usrConf)
{
return (isRoot == 1 &&
wolfSSHD_ConfigGetPermitRoot(usrConf) ==
WOLFSSHD_PERMIT_ROOT_FORCED_CMD &&
wolfSSHD_ConfigGetForcedCmd(usrConf) == NULL);
}

/* return WOLFSSH_USERAUTH_SUCCESS on success */
static int DoCheckUser(const char* usr, WOLFSSHD_AUTH* auth, int isRoot)
{
int ret = WOLFSSH_USERAUTH_SUCCESS;
int rc;
WOLFSSHD_CONFIG* usrConf;

wolfSSH_Log(WS_LOG_INFO, "[SSHD] Checking user name %s", usr);

if (isRoot == 1) {
/* Resolve per-user config so a Match override is honored; a NULL
* result is unresolvable, so fail closed and reject. */
usrConf = wolfSSHD_AuthGetUserConf(auth, usr, NULL, NULL, NULL, NULL,
NULL);
if (usrConf == NULL || wolfSSHD_ConfigGetPermitRoot(usrConf) == 0) {
if (usrConf == NULL || IsRootLoginDenied(isRoot, usrConf)) {
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Login as root not permitted");
ret = WOLFSSH_USERAUTH_REJECTED;
}
Expand Down Expand Up @@ -1934,6 +1967,7 @@ static int RequestAuthentication(WS_UserAuthData* authData,
{
int ret;
int rc;
int isRoot;
const char* usr;
WOLFSSHD_CONFIG* usrConf = NULL;

Expand All @@ -1948,7 +1982,9 @@ static int RequestAuthentication(WS_UserAuthData* authData,
}

usr = (const char*)authData->username;
ret = DoCheckUser(usr, authCtx);
isRoot = IsRootUser(usr);
ret = DoCheckUser(usr, authCtx, isRoot);

/* temporarily elevate permissions */
if (ret == WOLFSSH_USERAUTH_SUCCESS &&
wolfSSHD_AuthRaisePermissions(authCtx) != WS_SUCCESS) {
Expand Down Expand Up @@ -1983,12 +2019,17 @@ static int RequestAuthentication(WS_UserAuthData* authData,

if (ret == WOLFSSH_USERAUTH_SUCCESS &&
authData->type == WOLFSSH_USERAUTH_PASSWORD) {

if (wolfSSHD_ConfigGetPwAuth(usrConf) != 1) {
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Password authentication not "
"allowed by configuration!");
ret = WOLFSSH_USERAUTH_REJECTED;
}
else if (IsRootPasswordAuthBlocked(isRoot, usrConf)) {
/* prohibit-password and forced-commands-only both block this. */
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Password authentication for "
"root not allowed by configuration!");
ret = WOLFSSH_USERAUTH_REJECTED;
}
/* Check if password is valid for this user. */
/* first handle empty password cases */
else if (authData->sf.password.passwordSz == 0 &&
Expand All @@ -2000,6 +2041,7 @@ static int RequestAuthentication(WS_UserAuthData* authData,
else {
rc = authCtx->checkPasswordCb(usr, authData->sf.password.password,
authData->sf.password.passwordSz, authCtx);

if (rc == WSSHD_AUTH_SUCCESS) {
wolfSSH_Log(WS_LOG_INFO, "[SSHD] Password ok.");
}
Expand Down Expand Up @@ -2030,6 +2072,15 @@ static int RequestAuthentication(WS_UserAuthData* authData,
ret = WOLFSSH_USERAUTH_REJECTED;
}

if (ret == WOLFSSH_USERAUTH_SUCCESS &&
authData->type == WOLFSSH_USERAUTH_PUBLICKEY &&
IsRootPubKeyForcedCmdMissing(isRoot, usrConf)) {
/* forced-commands-only requires a forced command for root pubkey. */
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Public key login for root requires "
"a forced command by configuration!");
ret = WOLFSSH_USERAUTH_REJECTED;
}

#ifdef WOLFSSL_FPKI
if (ret == WOLFSSH_USERAUTH_SUCCESS &&
authData->type == WOLFSSH_USERAUTH_PUBLICKEY) {
Expand Down Expand Up @@ -2258,15 +2309,15 @@ int DefaultUserAuthTypes(WOLFSSH* ssh, void* ctx)
int ret = 0;

if (ssh == NULL || ctx == NULL)
return WS_BAD_ARGUMENT;
return 0;
authCtx = (WOLFSSHD_AUTH*)ctx;

/* get configuration for user */
usr = wolfSSH_GetUsername(ssh);
usrConf = wolfSSHD_AuthGetUserConf(authCtx, usr, NULL, NULL,
NULL, NULL, NULL);
if (usrConf == NULL) {
ret = WS_BAD_ARGUMENT;
ret = 0;
}
else {
ret = wolfSSHD_GetUserAuthTypes(usrConf);
Expand Down
5 changes: 4 additions & 1 deletion apps/wolfsshd/auth.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ int SearchForPubKey(const char* path, const char* authKeysFile,
WUID_T uid, int strictModes);
#endif
#if defined(WOLFSSH_HAVE_LIBCRYPT) || defined(WOLFSSH_HAVE_LIBLOGIN)
int CheckPasswordHashUnix(const char* input, char* stored);
int CheckPasswordHashUnix(const char* input, const char* stored);
#endif
int CheckAuthKeysLine(char* line, word32 lineSz, const byte* key,
word32 keySz);
Expand All @@ -128,6 +128,9 @@ int ResolveAuthKeysPath(const char* homeDir, const char* pattern,
int CAKeysFileDiffers(const char* a, const char* b);
int MatchUPNToUser(const char* usr, const char* name, int nameSz,
const char* allowList);
int IsRootLoginDenied(int isRoot, WOLFSSHD_CONFIG* usrConf);
int IsRootPasswordAuthBlocked(int isRoot, WOLFSSHD_CONFIG* usrConf);
int IsRootPubKeyForcedCmdMissing(int isRoot, WOLFSSHD_CONFIG* usrConf);
int wolfSSHD_GetUserAuthTypes(const WOLFSSHD_CONFIG* usrConf);
#if defined(WOLFSSH_OSSH_CERTS) && !defined(_WIN32)
int OsshPrefixMatch(const byte* a, const byte* b, int bits, int len);
Expand Down
16 changes: 13 additions & 3 deletions apps/wolfsshd/configuration.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ struct WOLFSSHD_CONFIG {
byte usePrivilegeSeparation:2;
byte passwordAuth:1;
byte pubKeyAuth:1;
byte permitRootLogin:1;
byte permitRootLogin:2;
byte permitEmptyPasswords:1;
byte authKeysFileSet:1; /* if not set then no explicit authorized keys */
byte strictModes:1; /* enforce file permission/ownership checks */
Expand Down Expand Up @@ -555,10 +555,20 @@ static int HandlePermitRoot(WOLFSSHD_CONFIG* conf, const char* value)

if (ret == WS_SUCCESS) {
if (WSTRCMP(value, "no") == 0) {
conf->permitRootLogin = 0;
conf->permitRootLogin = WOLFSSHD_PERMIT_ROOT_NO;
}
else if (WSTRCMP(value, "yes") == 0) {
conf->permitRootLogin = 1;
conf->permitRootLogin = WOLFSSHD_PERMIT_ROOT_YES;
}
else if (WSTRCMP(value, "prohibit-password") == 0 ||
WSTRCMP(value, "without-password") == 0) {
conf->permitRootLogin = WOLFSSHD_PERMIT_ROOT_PROHIBIT_PW;
}
else if (WSTRCMP(value, "forced-commands-only") == 0) {
wolfSSH_Log(WS_LOG_WARN, "[SSHD] PermitRootLogin "
"forced-commands-only: authorized_keys command= "
"restrictions not enforced");
conf->permitRootLogin = WOLFSSHD_PERMIT_ROOT_FORCED_CMD;
}
else {
ret = WS_BAD_ARGUMENT;
Expand Down
8 changes: 8 additions & 0 deletions apps/wolfsshd/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ typedef struct WOLFSSHD_CONFIG WOLFSSHD_CONFIG;
#define MAX_PATH_SZ 80
#endif

/* 0 so that root login is default off after struct memset'd on init */
#define WOLFSSHD_PERMIT_ROOT_NO 0
#define WOLFSSHD_PERMIT_ROOT_YES 1
#define WOLFSSHD_PERMIT_ROOT_PROHIBIT_PW 2
/* Prohibits passwords; pubkey logins require a configured ForceCommand
* (ignores authorized_keys command="..." restrictions). */
#define WOLFSSHD_PERMIT_ROOT_FORCED_CMD 3

WOLFSSHD_CONFIG* wolfSSHD_ConfigNew(void* heap);
void wolfSSHD_ConfigFree(WOLFSSHD_CONFIG* conf);
int wolfSSHD_ConfigLoad(WOLFSSHD_CONFIG* conf, const char* filename);
Expand Down
4 changes: 3 additions & 1 deletion apps/wolfsshd/test/run_all_sshd_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -396,12 +396,14 @@ else
run_test "sshd_window_full_test.sh"
run_test "sshd_empty_password_test.sh"
run_test "sshd_permitroot_test.sh"
run_test "sshd_permitroot_prohibit_password.sh"
run_test "sshd_permitroot_forced_cmd.sh"
run_strictmodes_negative_test
run_test "sshd_login_grace_test.sh"
run_test "sshd_privdrop_fail_test.sh"
else
printf "Skipping tests that need to setup local SSHD\n"
SKIPPED=$((SKIPPED+7))
SKIPPED=$((SKIPPED+9))
fi

# these tests run with X509 sshd-config loaded
Expand Down
Loading
Loading