Skip to content

Commit f9ced89

Browse files
committed
wolfsshd: hash passwords with crypt_r() if found
CheckPasswordHashUnix() hashes into a heap struct crypt_data when configure finds crypt_r(), and wipes the whole struct on return, so neither the result nor crypt's working state is left behind. Builds without it, such as macOS, keep crypt() and its result wipe. - configure probes crypt_r() through <crypt.h> as HAVE_CRYPT_R Issue: F-8842
1 parent dba37a8 commit f9ced89

2 files changed

Lines changed: 51 additions & 3 deletions

File tree

‎apps/wolfsshd/auth.c‎

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@
7575
#include <sys/stat.h>
7676
#include <pwd.h>
7777
#include <grp.h>
78+
#ifdef HAVE_CRYPT_R
79+
#include <crypt.h>
80+
#endif
7881
#include <errno.h>
7982
#include <fcntl.h>
8083
#include <unistd.h>
@@ -808,6 +811,13 @@ static int ExtractSalt(char* hash, char** salt, int saltSz)
808811
#endif
809812

810813
#if defined(WOLFSSH_HAVE_LIBCRYPT) || defined(WOLFSSH_HAVE_LIBLOGIN)
814+
#ifdef HAVE_CRYPT_R
815+
/* the result lives in data, which the caller wipes */
816+
#define WSSHD_CRYPT(in, salt, data) crypt_r((in), (salt), (data))
817+
#else
818+
#define WSSHD_CRYPT(in, salt, data) crypt((in), (salt))
819+
#endif
820+
811821
#ifdef WOLFSSHD_UNIT_TEST
812822
int CheckPasswordHashUnix(const char* input, const char* stored)
813823
#else
@@ -830,6 +840,12 @@ static int CheckPasswordHashUnix(const char* input, const char* stored)
830840
int locked = 0;
831841
char* hashedInput = NULL;
832842
word32 hashedInputSz = 0, storedSz = 0;
843+
#ifdef HAVE_CRYPT_R
844+
/* ~128 KB on glibc, ~32 KB on libxcrypt, so not on the stack */
845+
struct crypt_data* cryptData = NULL;
846+
#else
847+
void* cryptData = NULL;
848+
#endif
833849

834850
if (input == NULL || stored == NULL) {
835851
ret = WS_BAD_ARGUMENT;
@@ -843,6 +859,20 @@ static int CheckPasswordHashUnix(const char* input, const char* stored)
843859
return ret;
844860
}
845861

862+
#ifdef HAVE_CRYPT_R
863+
if (ret == WSSHD_AUTH_SUCCESS) {
864+
cryptData = (struct crypt_data*)WMALLOC(sizeof(*cryptData), NULL,
865+
DYNTYPE_SSHD);
866+
if (cryptData == NULL) {
867+
ret = WS_MEMORY_E;
868+
}
869+
else {
870+
/* zeroed, as crypt_r() requires of a fresh crypt_data */
871+
WMEMSET(cryptData, 0, sizeof(*cryptData));
872+
}
873+
}
874+
#endif
875+
846876
if (ret == WSSHD_AUTH_SUCCESS) {
847877
const char* salt = stored;
848878

@@ -870,7 +900,7 @@ static int CheckPasswordHashUnix(const char* input, const char* stored)
870900
}
871901
}
872902

873-
hashedInput = crypt(input, salt);
903+
hashedInput = WSSHD_CRYPT(input, salt, cryptData);
874904
/* glibc signals an unsupported salt with a "*"-prefixed sentinel,
875905
* not NULL; check both so the fallback engages on every libc. */
876906
if (locked) {
@@ -880,7 +910,7 @@ static int CheckPasswordHashUnix(const char* input, const char* stored)
880910
&& (hashedInput == NULL || hashedInput[0] == '*');
881911
fbIdx++) {
882912
salt = fakeHashFallbacks[fbIdx];
883-
hashedInput = crypt(input, salt);
913+
hashedInput = WSSHD_CRYPT(input, salt, cryptData);
884914
}
885915
}
886916

@@ -896,10 +926,18 @@ static int CheckPasswordHashUnix(const char* input, const char* stored)
896926
(const byte*)stored, storedSz) != 0) {
897927
ret = WSSHD_AUTH_FAILURE;
898928
}
899-
/* crypt() leaves the result in static storage */
929+
/* the result is in crypt()'s static storage or in cryptData */
900930
WS_FORCEZERO(hashedInput, hashedInputSz);
901931
}
902932
}
933+
#ifdef HAVE_CRYPT_R
934+
if (cryptData != NULL) {
935+
WS_FORCEZERO(cryptData, sizeof(*cryptData));
936+
WFREE(cryptData, NULL, DYNTYPE_SSHD);
937+
}
938+
#else
939+
WOLFSSH_UNUSED(cryptData);
940+
#endif
903941
return ret;
904942
}
905943
#endif /* WOLFSSH_HAVE_LIBCRYPT || WOLFSSH_HAVE_LIBLOGIN */

‎configure.ac‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,16 @@ AS_IF([test "x$ENABLED_SSHD" = "xyes"],[
436436
[AC_CHECK_LIB([crypt],[crypt],
437437
[AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_HAVE_LIBCRYPT"; LIBS="$LIBS -lcrypt"],
438438
[AC_MSG_ERROR(libcrypt not found)])])
439+
# crypt_r() keeps the hash in a buffer the daemon can wipe. Probe through
440+
# <crypt.h> so a positive result is build-safe; macOS has neither.
441+
AC_MSG_CHECKING([for crypt_r])
442+
AC_LINK_IFELSE(
443+
[AC_LANG_PROGRAM([[#include <crypt.h>]],
444+
[[struct crypt_data d; d.initialized = 0;
445+
(void)crypt_r("", "", &d);]])],
446+
[AC_DEFINE([HAVE_CRYPT_R], [1], [crypt_r() available and declared])
447+
AC_MSG_RESULT([yes])],
448+
[AC_MSG_RESULT([no])])
439449
])
440450
# Only the five-argument glibc/musl form; QNX and Solaris have four.
441451
AC_MSG_CHECKING([for getspnam_r])

0 commit comments

Comments
 (0)