@@ -360,20 +360,20 @@ static int ExtractSalt(char* hash, char** salt, int saltSz)
360360
361361#if defined(WOLFSSH_HAVE_LIBCRYPT ) || defined(WOLFSSH_HAVE_LIBLOGIN )
362362#ifdef WOLFSSHD_UNIT_TEST
363- int CheckPasswordHashUnix (const char * input , char * stored )
363+ int CheckPasswordHashUnix (const char * input , const char * stored )
364364#else
365- static int CheckPasswordHashUnix (const char * input , char * stored )
365+ static int CheckPasswordHashUnix (const char * input , const char * stored )
366366#endif
367367{
368368 int ret = WSSHD_AUTH_SUCCESS ;
369- char * hashedInput ;
369+ char * hashedInput = NULL ;
370370 word32 hashedInputSz = 0 , storedSz = 0 ;
371371
372372 if (input == NULL || stored == NULL ) {
373373 ret = WS_BAD_ARGUMENT ;
374374 }
375375
376- /* empty password case */
376+ /* empty password case: fast return based on real stored hash. */
377377 if (ret == WSSHD_AUTH_SUCCESS && stored [0 ] == 0 && WSTRLEN (input ) == 0 ) {
378378 wolfSSH_Log (WS_LOG_INFO ,
379379 "[SSHD] User logged in with empty password" );
@@ -1626,6 +1626,32 @@ static int RequestAuthentication(WS_UserAuthData* authData,
16261626
16271627 usr = (const char * )authData -> username ;
16281628 ret = DoCheckUser (usr , authCtx );
1629+
1630+ /* Avoid timing leak for invalid users with a fake crypt() call using a
1631+ * real $6$ (SHA-512) salt so it costs the same as a real lookup. */
1632+ if (ret == WOLFSSH_USERAUTH_INVALID_USER &&
1633+ authData -> type == WOLFSSH_USERAUTH_PASSWORD ) {
1634+ #if defined(WOLFSSH_HAVE_LIBCRYPT ) || defined(WOLFSSH_HAVE_LIBLOGIN )
1635+ word32 fakePwSz = authData -> sf .password .passwordSz ;
1636+ char * fakePwStr = (char * )WMALLOC (fakePwSz + 1 , NULL , DYNTYPE_STRING );
1637+
1638+ if (fakePwStr != NULL ) {
1639+ if (fakePwSz > 0 ) {
1640+ XMEMCPY (fakePwStr , authData -> sf .password .password , fakePwSz );
1641+ }
1642+ fakePwStr [fakePwSz ] = 0 ;
1643+ }
1644+
1645+ CheckPasswordHashUnix (fakePwStr != NULL ? fakePwStr : "" ,
1646+ "$6$wolfSSHDfakeSalt$" );
1647+
1648+ if (fakePwStr != NULL ) {
1649+ WS_FORCEZERO (fakePwStr , fakePwSz + 1 );
1650+ WFREE (fakePwStr , NULL , DYNTYPE_STRING );
1651+ }
1652+ #endif
1653+ }
1654+
16291655 /* temporarily elevate permissions */
16301656 if (ret == WOLFSSH_USERAUTH_SUCCESS &&
16311657 wolfSSHD_AuthRaisePermissions (authCtx ) != WS_SUCCESS ) {
@@ -1660,12 +1686,23 @@ static int RequestAuthentication(WS_UserAuthData* authData,
16601686
16611687 if (ret == WOLFSSH_USERAUTH_SUCCESS &&
16621688 authData -> type == WOLFSSH_USERAUTH_PASSWORD ) {
1689+ int configAllowed = 0 ;
16631690
16641691 if (wolfSSHD_ConfigGetPwAuth (usrConf ) != 1 ) {
16651692 wolfSSH_Log (WS_LOG_ERROR , "[SSHD] Password authentication not "
16661693 "allowed by configuration!" );
16671694 ret = WOLFSSH_USERAUTH_REJECTED ;
16681695 }
1696+ else if (XSTRCMP (usr , "root" ) == 0 &&
1697+ (wolfSSHD_ConfigGetPermitRoot (usrConf ) ==
1698+ WOLFSSHD_PERMIT_ROOT_PROHIBIT_PW ||
1699+ wolfSSHD_ConfigGetPermitRoot (usrConf ) ==
1700+ WOLFSSHD_PERMIT_ROOT_FORCED_CMD )) {
1701+ /* prohibit-password and forced-commands-only both block this. */
1702+ wolfSSH_Log (WS_LOG_ERROR , "[SSHD] Password authentication for "
1703+ "root not allowed by configuration!" );
1704+ ret = WOLFSSH_USERAUTH_REJECTED ;
1705+ }
16691706 /* Check if password is valid for this user. */
16701707 /* first handle empty password cases */
16711708 else if (authData -> sf .password .passwordSz == 0 &&
@@ -1675,8 +1712,14 @@ static int RequestAuthentication(WS_UserAuthData* authData,
16751712 ret = WOLFSSH_USERAUTH_FAILURE ;
16761713 }
16771714 else {
1715+ configAllowed = 1 ;
1716+ }
1717+
1718+ /* Only run password check when config allows it (avoids leaks). */
1719+ if (configAllowed ) {
16781720 rc = authCtx -> checkPasswordCb (usr , authData -> sf .password .password ,
1679- authData -> sf .password .passwordSz , authCtx );
1721+ authData -> sf .password .passwordSz , authCtx );
1722+
16801723 if (rc == WSSHD_AUTH_SUCCESS ) {
16811724 wolfSSH_Log (WS_LOG_INFO , "[SSHD] Password ok." );
16821725 }
@@ -1707,6 +1750,18 @@ static int RequestAuthentication(WS_UserAuthData* authData,
17071750 ret = WOLFSSH_USERAUTH_REJECTED ;
17081751 }
17091752
1753+ if (ret == WOLFSSH_USERAUTH_SUCCESS &&
1754+ authData -> type == WOLFSSH_USERAUTH_PUBLICKEY &&
1755+ XSTRCMP (usr , "root" ) == 0 &&
1756+ wolfSSHD_ConfigGetPermitRoot (usrConf ) ==
1757+ WOLFSSHD_PERMIT_ROOT_FORCED_CMD &&
1758+ wolfSSHD_ConfigGetForcedCmd (usrConf ) == NULL ) {
1759+ /* forced-commands-only requires a forced command for root pubkey login. */
1760+ wolfSSH_Log (WS_LOG_ERROR , "[SSHD] Public key login for root requires "
1761+ "a forced command by configuration!" );
1762+ ret = WOLFSSH_USERAUTH_REJECTED ;
1763+ }
1764+
17101765 #ifdef WOLFSSL_FPKI
17111766 if (ret == WOLFSSH_USERAUTH_SUCCESS &&
17121767 authData -> type == WOLFSSH_USERAUTH_PUBLICKEY ) {
@@ -1935,15 +1990,15 @@ int DefaultUserAuthTypes(WOLFSSH* ssh, void* ctx)
19351990 int ret = 0 ;
19361991
19371992 if (ssh == NULL || ctx == NULL )
1938- return WS_BAD_ARGUMENT ;
1993+ return 0 ;
19391994 authCtx = (WOLFSSHD_AUTH * )ctx ;
19401995
19411996 /* get configuration for user */
19421997 usr = wolfSSH_GetUsername (ssh );
19431998 usrConf = wolfSSHD_AuthGetUserConf (authCtx , usr , NULL , NULL ,
19441999 NULL , NULL , NULL );
19452000 if (usrConf == NULL ) {
1946- ret = WS_BAD_ARGUMENT ;
2001+ ret = 0 ;
19472002 }
19482003 else {
19492004 ret = wolfSSHD_GetUserAuthTypes (usrConf );
0 commit comments