From 7765b54b2b02df015073e463d2a6318bf5de2d76 Mon Sep 17 00:00:00 2001 From: Hayden Roche Date: Thu, 18 Aug 2022 14:22:10 -0700 Subject: [PATCH] Add support for OpenSSH-style certificates. This commit adds support for presenting an OpenSSH-style RSA SHA-1 cert as the host public key. It also adds support for receiving the same type of cert for user authentication. --- .gitignore | 2 +- apps/wolfsshd/auth.c | 70 +-- apps/wolfsshd/auth.h | 3 +- apps/wolfsshd/configuration.c | 17 +- apps/wolfsshd/include.am | 14 +- apps/wolfsshd/wolfsshd.c | 89 +-- configure.ac | 37 +- examples/echoserver/echoserver.c | 120 +++- keys/ossh/known_hosts | 1 + keys/ossh/ossh-host-ca | 27 + keys/ossh/ossh-host-ca.pub | 1 + keys/ossh/ossh-host-rsa-key | 27 + keys/ossh/ossh-host-rsa-key-cert.pub | 1 + keys/ossh/ossh-host-rsa-key.der | Bin 0 -> 1192 bytes keys/ossh/ossh-host-rsa-key.pub | 1 + keys/ossh/ossh-user-ca | 27 + keys/ossh/ossh-user-ca.pub | 1 + keys/ossh/ossh-user-rsa-key | 27 + keys/ossh/ossh-user-rsa-key-cert.pub | 1 + keys/ossh/ossh-user-rsa-key.pub | 1 + scripts/include.am | 7 + scripts/ossh-certs-setup.sh | 141 +++++ scripts/ossh-certs.test | 80 +++ src/include.am | 5 + src/internal.c | 868 ++++++++++++++++++++++----- src/list.c | 206 +++++++ src/misc.c | 107 ++++ src/ossh_certs.c | 726 ++++++++++++++++++++++ src/ssh.c | 93 ++- tests/api.c | 51 ++ tests/include.am | 34 ++ tests/test_list.c | 227 +++++++ tests/test_ossh_certs.c | 191 ++++++ wolfssh/error.h | 7 +- wolfssh/include.am | 5 +- wolfssh/internal.h | 32 +- wolfssh/list.h | 73 +++ wolfssh/misc.h | 12 + wolfssh/ossh_certs.h | 85 +++ wolfssh/ssh.h | 20 +- 40 files changed, 3109 insertions(+), 328 deletions(-) create mode 100644 keys/ossh/known_hosts create mode 100644 keys/ossh/ossh-host-ca create mode 100644 keys/ossh/ossh-host-ca.pub create mode 100644 keys/ossh/ossh-host-rsa-key create mode 100644 keys/ossh/ossh-host-rsa-key-cert.pub create mode 100644 keys/ossh/ossh-host-rsa-key.der create mode 100644 keys/ossh/ossh-host-rsa-key.pub create mode 100644 keys/ossh/ossh-user-ca create mode 100644 keys/ossh/ossh-user-ca.pub create mode 100644 keys/ossh/ossh-user-rsa-key create mode 100644 keys/ossh/ossh-user-rsa-key-cert.pub create mode 100644 keys/ossh/ossh-user-rsa-key.pub create mode 100755 scripts/ossh-certs-setup.sh create mode 100755 scripts/ossh-certs.test create mode 100644 src/list.c create mode 100644 src/ossh_certs.c create mode 100644 tests/test_list.c create mode 100644 tests/test_ossh_certs.c create mode 100644 wolfssh/list.h create mode 100644 wolfssh/ossh_certs.h diff --git a/.gitignore b/.gitignore index 865755744..ea50d46fe 100644 --- a/.gitignore +++ b/.gitignore @@ -58,7 +58,7 @@ examples/scpclient/wolfscp # applications apps/wolfsshd/wolfsshd -apps/wolfsshd/test/test_configuration +apps/wolfsshd/test/test_configuration.test # test output tests/*.test diff --git a/apps/wolfsshd/auth.c b/apps/wolfsshd/auth.c index 188c8ea0c..d5bb6ef4f 100644 --- a/apps/wolfsshd/auth.c +++ b/apps/wolfsshd/auth.c @@ -477,8 +477,7 @@ static int ResolveAuthKeysPath(const char* homeDir, char* resolved) } static int CheckPublicKeyUnix(const char* name, - const WS_UserAuthData_PublicKey* pubKeyCtx, - const char* usrCaKeysFile) + const WS_UserAuthData_PublicKey* pubKeyCtx) { int ret = WSSHD_AUTH_SUCCESS; int rc; @@ -493,65 +492,14 @@ static int CheckPublicKeyUnix(const char* name, #ifdef WOLFSSH_OSSH_CERTS if (pubKeyCtx->isOsshCert) { - byte* caKey = NULL; - word32 caKeySz; - const byte* caKeyType = NULL; - word32 caKeyTypeSz; - byte fingerprint[WC_SHA256_DIGEST_SIZE]; - - if (pubKeyCtx->caKey == NULL || - pubKeyCtx->caKeySz != WC_SHA256_DIGEST_SIZE) { - ret = WS_FATAL_ERROR; - } - - if (ret == WSSHD_AUTH_SUCCESS) { - f = XFOPEN(usrCaKeysFile, "rb"); - if (f == XBADFILE) { - wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Unable to open %s", - usrCaKeysFile); - ret = WS_BAD_FILE_E; - } - } - if (ret == WSSHD_AUTH_SUCCESS) { - lineBuf = (char*)WMALLOC(MAX_LINE_SZ, NULL, DYNTYPE_BUFFER); - if (lineBuf == NULL) { - ret = WS_MEMORY_E; - } - } - while (ret == WSSHD_AUTH_SUCCESS && - (current = XFGETS(lineBuf, MAX_LINE_SZ, f)) != NULL) { - currentSz = (word32)XSTRLEN(current); - - /* remove leading spaces */ - while (currentSz > 0 && current[0] == ' ') { - currentSz = currentSz - 1; - current = current + 1; - } - - if (currentSz <= 1) { - continue; /* empty line */ - } - - if (current[0] == '#') { - continue; /* commented out line */ - } - - rc = wolfSSH_ReadKey_buffer((const byte*)current, currentSz, - WOLFSSH_FORMAT_SSH, &caKey, &caKeySz, - &caKeyType, &caKeyTypeSz, NULL); - if (rc == WS_SUCCESS) { - rc = wc_Hash(WC_HASH_TYPE_SHA256, caKey, caKeySz, fingerprint, - WC_SHA256_DIGEST_SIZE); - if (rc == 0 && WMEMCMP(fingerprint, pubKeyCtx->caKey, - WC_SHA256_DIGEST_SIZE) == 0) { - foundKey = 1; - break; - } - } - } + /* + * wolfSSH proper will have already verified the user's cert if we've + * made it this far. There's no further checking to do in this callback. + */ + foundKey = 1; } else - #endif /* WOLFSSH_OSSH_CERTS */ +#endif /* WOLFSSH_OSSH_CERTS */ { errno = 0; pwInfo = getpwnam((const char*)name); @@ -632,7 +580,6 @@ static int CheckPublicKeyUnix(const char* name, WFREE(authKeysFile, NULL, DYNTYPE_STRING); } - (void)usrCaKeysFile; return ret; } #endif /* !_WIN32*/ @@ -750,8 +697,7 @@ static int RequestAuthentication(WS_UserAuthData* authData, } else { /* if not a certificate then parse through authorized key file */ - rc = authCtx->checkPublicKeyCb(usr, &authData->sf.publicKey, - wolfSSHD_ConfigGetUserCAKeysFile(authCtx->conf)); + rc = authCtx->checkPublicKeyCb(usr, &authData->sf.publicKey); if (rc == WSSHD_AUTH_SUCCESS) { wolfSSH_Log(WS_LOG_INFO, "[SSHD] Public key ok."); ret = WOLFSSH_USERAUTH_SUCCESS; diff --git a/apps/wolfsshd/auth.h b/apps/wolfsshd/auth.h index 9728c8599..cf67299c7 100644 --- a/apps/wolfsshd/auth.h +++ b/apps/wolfsshd/auth.h @@ -53,8 +53,7 @@ typedef int (*CallbackCheckPassword)(const char* usr, const byte* psw, * ok, and negative values if an error occurs during checking. */ typedef int (*CallbackCheckPublicKey)(const char* usr, - const WS_UserAuthData_PublicKey* pubKey, - const char* usrCaKeysFile); + const WS_UserAuthData_PublicKey* pubKey); WOLFSSHD_AUTH* wolfSSHD_AuthCreateUser(void* heap, const WOLFSSHD_CONFIG* conf); int wolfSSHD_AuthFreeUser(WOLFSSHD_AUTH* auth); diff --git a/apps/wolfsshd/configuration.c b/apps/wolfsshd/configuration.c index 94fdd19d4..1b946668f 100644 --- a/apps/wolfsshd/configuration.c +++ b/apps/wolfsshd/configuration.c @@ -284,16 +284,17 @@ void wolfSSHD_ConfigFree(WOLFSSHD_CONFIG* conf) while (current != NULL) { WOLFSSHD_CONFIG* next = current->next; heap = current->heap; - - FreeString(¤t->banner, heap); + + FreeString(¤t->banner, heap); FreeString(¤t->chrootDir, heap); - FreeString(¤t->ciphers, heap); - FreeString(¤t->kekAlgos, heap); - FreeString(¤t->hostKeyAlgos, heap); + FreeString(¤t->ciphers, heap); + FreeString(¤t->kekAlgos, heap); + FreeString(¤t->hostKeyAlgos, heap); FreeString(¤t->listenAddress, heap); - FreeString(¤t->authKeysFile, heap); - FreeString(¤t->hostKeyFile, heap); - FreeString(¤t->hostCertFile, heap); + FreeString(¤t->authKeysFile, heap); + FreeString(¤t->hostKeyFile, heap); + FreeString(¤t->hostCertFile, heap); + FreeString(¤t->userCAKeysFile, heap); WFREE(current, heap, DYNTYPE_SSHD); current = next; diff --git a/apps/wolfsshd/include.am b/apps/wolfsshd/include.am index 9a3d68c1e..8d3b06b2b 100644 --- a/apps/wolfsshd/include.am +++ b/apps/wolfsshd/include.am @@ -9,15 +9,17 @@ apps_wolfsshd_wolfsshd_SOURCES = apps/wolfsshd/wolfsshd.c \ apps_wolfsshd_wolfsshd_LDADD = src/libwolfssh.la apps_wolfsshd_wolfsshd_DEPENDENCIES = src/libwolfssh.la -noinst_PROGRAMS += apps/wolfsshd/test/test_configuration -apps_wolfsshd_test_test_configuration_SOURCES = apps/wolfsshd/test/test_configuration.c \ +noinst_PROGRAMS += apps/wolfsshd/test/test_configuration.test +check_PROGRAMS += apps/wolfsshd/test/test_configuration.test +apps_wolfsshd_test_test_configuration_test_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 = $(AM_CPPFLAGS) -DWOLFSSH_SSHD -DWOLFSSHD_UNIT_TEST -I$(srcdir)/apps/wolfsshd/ + +apps_wolfsshd_test_test_configuration_test_LDADD = src/libwolfssh.la +apps_wolfsshd_test_test_configuration_test_DEPENDENCIES = src/libwolfssh.la +apps_wolfsshd_test_test_configuration_test_CPPFLAGS = $(AM_CPPFLAGS) -DWOLFSSH_SSHD -DWOLFSSHD_UNIT_TEST -I$(srcdir)/apps/wolfsshd/ DISTCLEANFILES+= apps/wolfsshd/.libs/wolfsshd \ - apps/wolfsshd/test/.libs/test_configuration + apps/wolfsshd/test/.libs/test_configuration.test endif BUILD_SSHD diff --git a/apps/wolfsshd/wolfsshd.c b/apps/wolfsshd/wolfsshd.c index f7b743602..570521f13 100644 --- a/apps/wolfsshd/wolfsshd.c +++ b/apps/wolfsshd/wolfsshd.c @@ -189,7 +189,7 @@ static void CleanupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx) } #ifndef NO_FILESYSTEM -static void freeBufferFromFile(byte* buf, void* heap) +static void FreeBufferFromFile(byte* buf, void* heap) { if (buf != NULL) WFREE(buf, heap, DYNTYPE_SSHD); @@ -198,7 +198,7 @@ static void freeBufferFromFile(byte* buf, void* heap) /* set bufSz to size wanted if too small and buf is null */ -static byte* getBufferFromFile(const char* fileName, word32* bufSz, void* heap) +static byte* GetBufferFromFile(const char* fileName, word32* bufSz, void* heap) { FILE* file; byte* buf = NULL; @@ -282,7 +282,7 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx) byte* data; word32 dataSz = 0; - data = getBufferFromFile(hostKey, &dataSz, heap); + data = GetBufferFromFile(hostKey, &dataSz, heap); if (data == NULL) { wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error reading host key file."); @@ -311,22 +311,22 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx) ret = WS_BAD_ARGUMENT; } - freeBufferFromFile(data, heap); + FreeBufferFromFile(data, heap); wc_FreeDer(&der); } } } - #if defined(WOLFSSH_OSSH_CERTS) || defined(WOLFSSH_CERTS) if (ret == WS_SUCCESS) { - /* TODO: Create a helper function that uses a file instead. */ char* hostCert = wolfSSHD_ConfigGetHostCertFile(conf); if (hostCert != NULL) { byte* data; word32 dataSz = 0; - data = getBufferFromFile(hostCert, &dataSz, heap); + wolfSSH_Log(WS_LOG_INFO, "[SSHD] Using host cert file %s", + hostCert); + data = GetBufferFromFile(hostCert, &dataSz, heap); if (data == NULL) { wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error reading host key file."); @@ -335,44 +335,48 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx) } if (ret == WS_SUCCESS) { - #ifdef WOLFSSH_OPENSSH_CERTS - if (wolfSSH_CTX_UseOsshCert_buffer(*ctx, data, dataSz) < 0) { - wolfSSH_Log(WS_LOG_ERROR, - "[SSHD] Failed to use host certificate."); - ret = WS_BAD_ARGUMENT; - } - #endif + #ifdef WOLFSSH_OSSH_CERTS + ret = wolfSSH_CTX_UseOsshCert_buffer(*ctx, data, dataSz); + /* + * If wolfSSH_CTX_UseOsshCert_buffer failed, the cert might be + * X.509. Try with wolfSSH_CTX_UseCert_buffer. + */ + if (ret != WS_SUCCESS) + #endif /* WOLFSSH_OSSH_CERTS */ + { #ifdef WOLFSSH_CERTS - if (ret == WS_SUCCESS || ret == WS_BAD_ARGUMENT) { + /* Try PEM first. */ ret = wolfSSH_CTX_UseCert_buffer(*ctx, data, dataSz, WOLFSSH_FORMAT_PEM); if (ret != WS_SUCCESS) { + /* Try DER (ASN.1) if it wasn't PEM. */ ret = wolfSSH_CTX_UseCert_buffer(*ctx, data, dataSz, WOLFSSH_FORMAT_ASN1); } - if (ret != WS_SUCCESS) { - wolfSSH_Log(WS_LOG_ERROR, - "[SSHD] Failed to load in host certificate."); - } + #endif /* WOLFSSH_CERTS */ + } + if (ret != WS_SUCCESS) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failed to load in host " + "certificate %s.", hostCert); } - #endif - freeBufferFromFile(data, heap); + FreeBufferFromFile(data, heap); } } } #endif /* WOLFSSH_OSSH_CERTS || WOLFSSH_CERTS */ -#ifdef WOLFSSH_CERTS +#if defined(WOLFSSH_CERTS) || defined(WOLFSSH_OSSH_CERTS) if (ret == WS_SUCCESS) { char* caCert = wolfSSHD_ConfigGetUserCAKeysFile(conf); + if (caCert != NULL) { byte* data; word32 dataSz = 0; - - wolfSSH_Log(WS_LOG_INFO, "[SSHD] Using CA keys file %s", caCert); - data = getBufferFromFile(caCert, &dataSz, heap); + wolfSSH_Log(WS_LOG_INFO, "[SSHD] Using user CA keys file %s", + caCert); + data = GetBufferFromFile(caCert, &dataSz, heap); if (data == NULL) { wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error reading CA cert file."); @@ -381,25 +385,32 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx) } if (ret == WS_SUCCESS) { - ret = wolfSSH_CTX_AddRootCert_buffer(*ctx, data, dataSz, - WOLFSSH_FORMAT_PEM); - if (ret != WS_SUCCESS) { + #ifdef WOLFSSH_OSSH_CERTS + ret = wolfSSH_CTX_AddOsshCAKey(*ctx, data, dataSz); + /* + * If wolfSSH_CTX_AddOsshCAKey failed, try + * wolfSSH_CTX_AddRootCert_buffer. + */ + if (ret != WS_SUCCESS) + #endif /* WOLFSSH_OSSH_CERTS */ + { + #ifdef WOLFSSH_CERTS + /* Try PEM first. */ ret = wolfSSH_CTX_AddRootCert_buffer(*ctx, data, dataSz, - WOLFSSH_FORMAT_ASN1); + WOLFSSH_FORMAT_PEM); + if (ret != WS_SUCCESS) { + /* Try DER (ASN.1) if it wasn't PEM. */ + ret = wolfSSH_CTX_AddRootCert_buffer(*ctx, data, dataSz, + WOLFSSH_FORMAT_ASN1); + } + #endif /* WOLFSSH_CERTS */ } if (ret != WS_SUCCESS) { - #ifdef WOLFSSH_OPENSSH_CERTS - wolfSSH_Log(WS_LOG_INFO, - "[SSHD] Continuing on in case CA is openssh " - "style."); - ret = WS_SUCCESS; - #else - wolfSSH_Log(WS_LOG_ERROR, - "[SSHD] Failed to load in CA certificate."); - #endif + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failed to load in user CA" + " keys file %s.", caCert); } - freeBufferFromFile(data, heap); + FreeBufferFromFile(data, heap); } } } diff --git a/configure.ac b/configure.ac index c4b78f09e..49f7af25b 100644 --- a/configure.ac +++ b/configure.ac @@ -206,6 +206,11 @@ AC_ARG_ENABLE([smallstack], [AS_HELP_STRING([--enable-smallstack],[Enable small stack (default: disabled)])], [ENABLED_SMALLSTACK=$enableval],[ENABLED_SMALLSTACK=no]) +# OpenSSH-style certificates +AC_ARG_ENABLE([openssh-certs], + [AS_HELP_STRING([--enable-openssh-certs],[Enable support for OpenSSH-style certificates (default: disabled)])], + [ENABLED_OPENSSH_CERTS=$enableval],[ENABLED_OPENSSH_CERTS=no]) + # use PAM lib AC_ARG_WITH([pam], [AS_HELP_STRING([--with-pam=PATH],[PATH to directory with the PAM library])], @@ -226,7 +231,7 @@ AC_ARG_ENABLE([distro], AS_IF([test "x$ENABLED_DISTRO" = "xyes"], [ENABLED_ALL=yes; enable_shared=yes; enable_static=yes]) AS_IF([test "x$ENABLED_ALL" = "xyes"], - [ENABLED_KEYGEN=yes; ENABLED_SCP=yes; ENABLED_SFTP=yes; ENABLED_FWD=yes; ENABLED_SHELL=yes; ENABLED_AGENT=yes; ENABLED_SSHD=yes; ENABLED_CERTS=yes]) + [ENABLED_KEYGEN=yes; ENABLED_SCP=yes; ENABLED_SFTP=yes; ENABLED_FWD=yes; ENABLED_SHELL=yes; ENABLED_AGENT=yes; ENABLED_SSHD=yes; ENABLED_CERTS=yes; ENABLED_OPENSSH_CERTS=yes;]) AS_IF([test "x$ENABLED_SSHD" = "xyes"], [ENABLED_SHELL=yes]) @@ -251,6 +256,8 @@ AS_IF([test "x$ENABLED_SMALLSTACK" = "xyes"], [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_SMALL_STACK"]) AS_IF([test "x$ENABLED_SSHD" = "xyes"], [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_SSHD"]) +AS_IF([test "x$ENABLED_OPENSSH_CERTS" = "xyes"], + [AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_OSSH_CERTS"]) if test "$ENABLED_SSHD" = "yes"; then if test -n "$PAM_LIB" @@ -304,6 +311,7 @@ AM_CONDITIONAL([BUILD_SHELL],[test "x$ENABLED_SHELL" = "xyes"]) AM_CONDITIONAL([BUILD_AGENT],[test "x$ENABLED_AGENT" = "xyes"]) AM_CONDITIONAL([BUILD_SSHD],[test "x$ENABLED_SSHD" = "xyes"]) AM_CONDITIONAL([BUILD_CERTS],[test "x$ENABLED_CERTS" = "xyes"]) +AM_CONDITIONAL([BUILD_OPENSSH_CERTS],[test "x$ENABLED_OPENSSH_CERTS" = "xyes"]) AX_HARDEN_CC_COMPILER_FLAGS @@ -337,16 +345,17 @@ AS_ECHO([" * CPP Flags: $CPPFLAGS"]) AS_ECHO([" * Linker Flags: $LDFLAGS"]) AS_ECHO AS_ECHO([" Features"]) -AS_ECHO([" * Inline Code: $ENABLED_INLINE"]) -AS_ECHO([" * Small stack: $ENABLED_SMALLSTACK"]) -AS_ECHO([" * keygen: $ENABLED_KEYGEN"]) -AS_ECHO([" * psuedo-terminal: $ENABLED_PTERM"]) -AS_ECHO([" * echoserver shell support: $ENABLED_SHELL"]) -AS_ECHO([" * scp: $ENABLED_SCP"]) -AS_ECHO([" * sftp: $ENABLED_SFTP"]) -AS_ECHO([" * sshd: $ENABLED_SSHD"]) -AS_ECHO([" * agent: $ENABLED_AGENT"]) -AS_ECHO([" * TCP/IP Forwarding: $ENABLED_FWD"]) -AS_ECHO([" * X.509 Certs: $ENABLED_CERTS"]) -AS_ECHO([" * Examples: $ENABLED_EXAMPLES"]) -AS_ECHO([" * liboqs Integration: $ENABLED_LIBOQS"]) +AS_ECHO([" * Inline Code: $ENABLED_INLINE"]) +AS_ECHO([" * Small stack: $ENABLED_SMALLSTACK"]) +AS_ECHO([" * keygen: $ENABLED_KEYGEN"]) +AS_ECHO([" * psuedo-terminal: $ENABLED_PTERM"]) +AS_ECHO([" * echoserver shell support: $ENABLED_SHELL"]) +AS_ECHO([" * scp: $ENABLED_SCP"]) +AS_ECHO([" * sftp: $ENABLED_SFTP"]) +AS_ECHO([" * sshd: $ENABLED_SSHD"]) +AS_ECHO([" * agent: $ENABLED_AGENT"]) +AS_ECHO([" * TCP/IP Forwarding: $ENABLED_FWD"]) +AS_ECHO([" * X.509 Certs: $ENABLED_CERTS"]) +AS_ECHO([" * OpenSSH-style certs: $ENABLED_OPENSSH_CERTS"]) +AS_ECHO([" * Examples: $ENABLED_EXAMPLES"]) +AS_ECHO([" * liboqs Integration: $ENABLED_LIBOQS"]) diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index 65712ff0b..04c52bddc 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -2150,16 +2150,21 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) const char* defaultSftpPath = NULL; char nonBlock = 0; char* userPubKey = NULL; - #ifdef WOLFSSH_CERTS - char* caCert = NULL; - #endif +#ifdef WOLFSSH_CERTS + char* caCert = NULL; +#endif +#ifdef WOLFSSH_OSSH_CERTS + char* osshCert = NULL; + char* osshCaKey = NULL; +#endif + char* privKey = NULL; int argc = serverArgs->argc; char** argv = serverArgs->argv; serverArgs->return_code = 0; if (argc > 0) { - while ((ch = mygetopt(argc, argv, "?1a:d:efEp:R:Ni:j:I:J:K:P:")) != -1) { + while ((ch = mygetopt(argc, argv, "?1a:d:efEp:R:Ni:j:I:J:K:P:C:T:k:")) != -1) { switch (ch) { case '?' : ShowUsage(); @@ -2170,9 +2175,9 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) break; case 'a': - #ifdef WOLFSSH_CERTS - caCert = myoptarg; - #endif + #ifdef WOLFSSH_CERTS + caCert = myoptarg; + #endif break; case 'e' : userEcc = 1; @@ -2233,6 +2238,22 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) passwdList = StrListAdd(passwdList, myoptarg); break; + case 'C': + #ifdef WOLFSSH_OSSH_CERTS + osshCert = myoptarg; + #endif + break; + + case 'T': + #ifdef WOLFSSH_OSSH_CERTS + osshCaKey = myoptarg; + #endif + break; + + case 'k': + privKey = myoptarg; + break; + default: ShowUsage(); WEXIT(MY_EX_USAGE); @@ -2312,23 +2333,40 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) byte buf[EXAMPLE_KEYLOAD_BUFFER_SZ]; #endif byte* keyLoadBuf; - word32 bufSz; + word32 bufSz = 0; - #ifdef WOLFSSH_SMALL_STACK - keyLoadBuf = (byte*)WMALLOC(EXAMPLE_KEYLOAD_BUFFER_SZ, - NULL, 0); + if (privKey != NULL) { + load_file(privKey, NULL, &bufSz); + if (bufSz == 0) { + fprintf(stderr, "Couldn't find size of file %s.\n", privKey); + WEXIT(EXIT_FAILURE); + } + + keyLoadBuf = (byte*)WMALLOC(bufSz, NULL, 0); if (keyLoadBuf == NULL) { + fprintf(stderr, "WMALLOC for private key buffer failed.\n"); WEXIT(EXIT_FAILURE); } - #else - keyLoadBuf = buf; - #endif - bufSz = EXAMPLE_KEYLOAD_BUFFER_SZ; - bufSz = load_key(peerEcc, keyLoadBuf, bufSz); - if (bufSz == 0) { - fprintf(stderr, "Couldn't load key file.\n"); - WEXIT(EXIT_FAILURE); + load_file(privKey, keyLoadBuf, &bufSz); + } + else { + #ifdef WOLFSSH_SMALL_STACK + keyLoadBuf = (byte*)WMALLOC(EXAMPLE_KEYLOAD_BUFFER_SZ, + NULL, 0); + if (keyLoadBuf == NULL) { + WEXIT(EXIT_FAILURE); + } + #else + keyLoadBuf = buf; + #endif + bufSz = EXAMPLE_KEYLOAD_BUFFER_SZ; + + bufSz = load_key(peerEcc, keyLoadBuf, bufSz); + if (bufSz == 0) { + fprintf(stderr, "Couldn't load key file.\n"); + WEXIT(EXIT_FAILURE); + } } if (wolfSSH_CTX_UsePrivateKey_buffer(ctx, keyLoadBuf, bufSz, WOLFSSH_FORMAT_ASN1) < 0) { @@ -2359,7 +2397,7 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) WFREE(userBuf, NULL, 0); } - #ifdef WOLFSSH_CERTS + #ifdef WOLFSSH_CERTS if (caCert) { byte* certBuf = NULL; word32 certBufSz = 0; @@ -2381,13 +2419,51 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) load_file(caCert, certBuf, &certBufSz); ret = wolfSSH_CTX_AddRootCert_buffer(ctx, certBuf, certBufSz, WOLFSSH_FORMAT_PEM); - if (ret != 0) { + if (ret != WS_SUCCESS) { fprintf(stderr, "Couldn't add root cert\n"); WEXIT(EXIT_FAILURE); } WFREE(certBuf, NULL, 0); } - #endif + #endif /* WOLFSSH_CERTS */ + + #ifdef WOLFSSH_OSSH_CERTS + if (osshCert) { + byte* certBuf = NULL; + word32 certBufSz = 0; + int ret = 0; + + load_file(osshCert, NULL, &certBufSz); + + if (certBufSz == 0) { + fprintf(stderr, + "Couldn't find size of file %s.\n", osshCert); + WEXIT(EXIT_FAILURE); + } + + certBuf = (byte*)WMALLOC(certBufSz, NULL, 0); + if (certBuf == NULL) { + fprintf(stderr, "WMALLOC for OpenSSH-style cert buffer " + "failed.\n"); + WEXIT(EXIT_FAILURE); + } + + load_file(osshCert, certBuf, &certBufSz); + ret = wolfSSH_CTX_UseOsshCert_buffer(ctx, certBuf, certBufSz); + WFREE(certBuf, NULL, 0); + if (ret != WS_SUCCESS) { + fprintf(stderr, "wolfSSH_CTX_UseOsshCert_buffer failed.\n"); + WEXIT(EXIT_FAILURE); + } + } + + if (osshCaKey) { + if (wolfSSH_CTX_AddOsshCAKeys_file(ctx, osshCaKey) != WS_SUCCESS) { + fprintf(stderr, "wolfSSH_CTX_AddOsshCAKeys_file failed.\n"); + WEXIT(EXIT_FAILURE); + } + } + #endif /* WOLFSSH_OSSH_CERTS */ bufSz = (word32)WSTRLEN(samplePasswordBuffer); WMEMCPY(keyLoadBuf, samplePasswordBuffer, bufSz); diff --git a/keys/ossh/known_hosts b/keys/ossh/known_hosts new file mode 100644 index 000000000..09cd18ed2 --- /dev/null +++ b/keys/ossh/known_hosts @@ -0,0 +1 @@ +@cert-authority localhost ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCrZm5iiLptChGpx3Rp1aC6D9H7PCRgDsYhEOwiH4nlXre2SRPp1Hl1JIVK9te0hmKPatVu5EVIEc9ta0MEzOzIxsAzw/TYrFDuokHo+cTK+DmIXF/DJrhJZ9BXqIjlzXBDcPnfgdSkIe9eaJUQH6KRnZ9wKZsnZA0gxO6nt40/EPM3dnGfGq+WrXoocccbPKfPpuhGw3yXMhxt4bEM2IzhCw8gpHC5v5cAF0PWitcZA8hU9vN7WYOBXdB1pacrlVIhVFFYQDuJ/Z+XGYfs57u2XpllTHyXQOKTMVn5DMZVebLkHtFcZuG7rqgKYIVzR7FC+fQ3pF0h0SRouZcdp2Af ossh-host-ca diff --git a/keys/ossh/ossh-host-ca b/keys/ossh/ossh-host-ca new file mode 100644 index 000000000..b7360c345 --- /dev/null +++ b/keys/ossh/ossh-host-ca @@ -0,0 +1,27 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn +NhAAAAAwEAAQAAAQEAq2ZuYoi6bQoRqcd0adWgug/R+zwkYA7GIRDsIh+J5V63tkkT6dR5 +dSSFSvbXtIZij2rVbuRFSBHPbWtDBMzsyMbAM8P02KxQ7qJB6PnEyvg5iFxfwya4SWfQV6 +iI5c1wQ3D534HUpCHvXmiVEB+ikZ2fcCmbJ2QNIMTup7eNPxDzN3Zxnxqvlq16KHHHGzyn +z6boRsN8lzIcbeGxDNiM4QsPIKRwub+XABdD1orXGQPIVPbze1mDgV3QdaWnK5VSIVRRWE +A7if2flxmH7Oe7tl6ZZUx8l0DikzFZ+QzGVXmy5B7RXGbhu66oCmCFc0exQvn0N6RdIdEk +aLmXHadgHwAAA8gj8JVPI/CVTwAAAAdzc2gtcnNhAAABAQCrZm5iiLptChGpx3Rp1aC6D9 +H7PCRgDsYhEOwiH4nlXre2SRPp1Hl1JIVK9te0hmKPatVu5EVIEc9ta0MEzOzIxsAzw/TY +rFDuokHo+cTK+DmIXF/DJrhJZ9BXqIjlzXBDcPnfgdSkIe9eaJUQH6KRnZ9wKZsnZA0gxO +6nt40/EPM3dnGfGq+WrXoocccbPKfPpuhGw3yXMhxt4bEM2IzhCw8gpHC5v5cAF0PWitcZ +A8hU9vN7WYOBXdB1pacrlVIhVFFYQDuJ/Z+XGYfs57u2XpllTHyXQOKTMVn5DMZVebLkHt +FcZuG7rqgKYIVzR7FC+fQ3pF0h0SRouZcdp2AfAAAAAwEAAQAAAQBHMGAgpNFpgIkdjy4W +1xfOr8tzIeOp0fkQusqY0aigj9qV53xR3mIj+WszW5rz6+z7zE+ho0XzTRJuS+KmF8cxDP +u/hGAprkhfFR1y+mp2MusSa+uKToa0Vl3+So2xhMZf/IiMAIylL0MQXzeqXrlS6bjTCTTc +A35AYgsNtfsAegewlrk01TDik9DwPUZNzj00jF8L85n+zy9r0AJzSP/ytSddk58g1NToVv +jaWCPS0azWTnFfsL84FN7pTnGkbaMjgT3KrEBwI0AhIiK/nmpMhR2dJ4L6AbJLeC61axxU +sHvZq6i/LOFstIc3zXAPdRLzTdGSEK2kmCpLXqZAkA9BAAAAgCrMGAsUjivK6Z5VCo1H25 +/WFwrR9VkxETgIJg5aLCjr7H3oTvFY7TMxa1UhXbF/fp32Y7u+p/7vW/iCY/KS6eVa5pUI +tpx0H3L9DSda09+LbL4EeYX/bgcDzMAxVZIlpN7YKDryVj8PoMAwb84hiKJld7dlgIE2aU +oocRhKBGx8AAAAgQDYj0VryxjbIZ+FVNkD7Agt9BEqFY+K822/Niaw2pZo6afxpsYi4+5o +LvhCsJbZFzLjLBovU9gjAk9xnJIPksQxQ2wpkWEdtE9CrWEOLTLo5HznK2tO5akPRkdVej +pR+CZIF+1GWFh73czHt5kr15INbE7QYgwGTtyySOzd3/sKNwAAAIEAyp2sSN6+ZljyPEPo +TbUpnQFz7Unk2YDFGHV4ITuX2D3DQDXv8N+ZOfqULX04g624+PKVl+TS6I7RXH69Vz2iXx +2+T3dTLkVNAF2ay4RU/SbU7FOjZ/1QiKcQhPNZkSUwptsFKsI+ns69r+dxxGa7EnlAl/du +bxObCPHbh8dgRVkAAAAMb3NzaC1ob3N0LWNhAQIDBAUGBw== +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/ossh/ossh-host-ca.pub b/keys/ossh/ossh-host-ca.pub new file mode 100644 index 000000000..ee8cb7939 --- /dev/null +++ b/keys/ossh/ossh-host-ca.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCrZm5iiLptChGpx3Rp1aC6D9H7PCRgDsYhEOwiH4nlXre2SRPp1Hl1JIVK9te0hmKPatVu5EVIEc9ta0MEzOzIxsAzw/TYrFDuokHo+cTK+DmIXF/DJrhJZ9BXqIjlzXBDcPnfgdSkIe9eaJUQH6KRnZ9wKZsnZA0gxO6nt40/EPM3dnGfGq+WrXoocccbPKfPpuhGw3yXMhxt4bEM2IzhCw8gpHC5v5cAF0PWitcZA8hU9vN7WYOBXdB1pacrlVIhVFFYQDuJ/Z+XGYfs57u2XpllTHyXQOKTMVn5DMZVebLkHtFcZuG7rqgKYIVzR7FC+fQ3pF0h0SRouZcdp2Af ossh-host-ca diff --git a/keys/ossh/ossh-host-rsa-key b/keys/ossh/ossh-host-rsa-key new file mode 100644 index 000000000..9bc26883e --- /dev/null +++ b/keys/ossh/ossh-host-rsa-key @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEpAIBAAKCAQEA8gKEgBtvKaPeS0y7I5rCjT56GvPm/hlTHJTLInvUPNXZrUgJ +UKHm5O+tRkTAVILq16nYEVFU1eLdvXCAHYsMcs8Fi7zAZpJUEEaL06fKIbx2YJ1s +Y5TuO3pN1KriYAMBsEtp/raIqaYvffNomf5npj57BECahF1BuCzFRRiNQHWFd2AE +v2LNblwfylNVnRMQbqjpHjbI4dXi8drW2iyvyZv3+uxeF9KavDnBp9BLXfRNPCK+ +i2WjbmqNpLBx7zCGuyAJwuWHy9kOWj02Ip+1bjjE+2ILDs9YABJR4wHWkj/F3ksc +jqVU24H2INWRFg/uqeYCjQJ37kn/z4viSLIXsQIDAQABAoIBAQCcGjWfcJK+mD3N +8luPtsahVthqREMOSgWLHQr+XkNjTkmPdTTU9um5aEQrXDN3D88tdxew6/Y91I1V +IZjx9Xv9Hj4kiYbNYruol+ifiM5f/nGZFlIQ1cLpSJWlhOhqJ+ZR8gMX0mPKkjB2 +HKivFCOFXy34azeAA6tdgjtou1J5bVOi1pnpRf+hV1h6ayKeA2iJsxMAcRKvoWNf +s1nWol1ZzQeRarbTJ5sSgwVNmJZUIA/CjiJMb8/rw38jyGGUW/k0Xh+VR+v5x4iI +QNp65NibyZBhf/3OB7Q8x8N9QLk3QVhb2ve73NTpReNYjKdNycXM4x2ICEZlEXEj +wuWpoYX1AoGBAPnfaV+KNlNoaEEdMZ9HdQADJJ7FGmmkJApB584xJSP3/C1+GrTa +HCClLoWjfJwZQ3BlWp3EWLvAZud4DvZd7UcwP85ESwXS+MWk2iWbY795dnhUAEvA +mnP3ttbOwpM4C3W3+kcLolIiccQKSBlnFAMqd8Qn95dyPyDXC6uBLBcjAoGBAPfx +vxWOm4FyGTxU8sj5OQlk/zbhok8dsezVrE3TEv3qpQ6uGZjtZa2VGKrgyR7hPSp1 +nzcmie0mmKeNqr74jGAjoiRN3ODn54sKMnlNb1qbYRh2w1RuaJ5Qgcz8FThqY2fp +ZdHzsjC3da0fZnZnL2W4MV+j+3x//ewZPqaIA60bAoGAIxeRQO20O3qKSbrD4U6z +y9ClL/cWgoee2CHxYC/eu3J0ZB81uPh4wszv/6y6L2IM6pgRZd7RZ2zaBuABmfAP +BtRr4ZgNwT+j2H4SdP2hwJ5aWQPTFtBx4J+Feh2Hjg1s7pdr+tZTcZ2MxK98+LRV +RyyLrWy/hPFylDYfWXLGsmkCgYBxsO0g0mJl6c6+hmlJVFYeA6yvGqlVQ9WJNrUq +fmXP/p8u6xAbuoeMH7jO6gHu4fMtHAAkxswp7S/EbMZliQy9LLm1kep9Snk2nTdl +vecYkfTkxdhtR687IMlUGCcW1lm8lR42Nlt30TfpaWOPWWM3bk1dgJDZlsr1TuIW +dHnTDwKBgQClauIqm3M7CsTUm+NcUNNes+57qKx8pdeW4KmRFpcHRLII3Dh9ZwBF +SsA7j51+RAO+ernQtcptge3kvRGrliD5KXjbodYz+7A37Iqtocl9VYrereWPD504 +EY45RLIXSaC3KN1r/p330uuJ3qnq3NbqqRo3WGKen4Oh9/jbZnqd2Q== +-----END RSA PRIVATE KEY----- diff --git a/keys/ossh/ossh-host-rsa-key-cert.pub b/keys/ossh/ossh-host-rsa-key-cert.pub new file mode 100644 index 000000000..8d175d7a5 --- /dev/null +++ b/keys/ossh/ossh-host-rsa-key-cert.pub @@ -0,0 +1 @@ +ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAg0G6KtQaxnxpFQ9JfeLAgAqYi1BTPZTpUmqfidO3HjCQAAAADAQABAAABAQDyAoSAG28po95LTLsjmsKNPnoa8+b+GVMclMsie9Q81dmtSAlQoebk761GRMBUgurXqdgRUVTV4t29cIAdiwxyzwWLvMBmklQQRovTp8ohvHZgnWxjlO47ek3UquJgAwGwS2n+toippi9982iZ/memPnsEQJqEXUG4LMVFGI1AdYV3YAS/Ys1uXB/KU1WdExBuqOkeNsjh1eLx2tbaLK/Jm/f67F4X0pq8OcGn0Etd9E08Ir6LZaNuao2ksHHvMIa7IAnC5YfL2Q5aPTYin7VuOMT7YgsOz1gAElHjAdaSP8XeSxyOpVTbgfYg1ZEWD+6p5gKNAnfuSf/Pi+JIshexAAAAAAAAAAAAAAACAAAACWxvY2FsaG9zdAAAAA0AAAAJbG9jYWxob3N0AAAAAAAAAAD//////////wAAAAAAAAAAAAAAAAAAARcAAAAHc3NoLXJzYQAAAAMBAAEAAAEBAKtmbmKIum0KEanHdGnVoLoP0fs8JGAOxiEQ7CIfieVet7ZJE+nUeXUkhUr217SGYo9q1W7kRUgRz21rQwTM7MjGwDPD9NisUO6iQej5xMr4OYhcX8MmuEln0FeoiOXNcENw+d+B1KQh715olRAfopGdn3ApmydkDSDE7qe3jT8Q8zd2cZ8ar5ateihxxxs8p8+m6EbDfJcyHG3hsQzYjOELDyCkcLm/lwAXQ9aK1xkDyFT283tZg4Fd0HWlpyuVUiFUUVhAO4n9n5cZh+znu7ZemWVMfJdA4pMxWfkMxlV5suQe0Vxm4buuqApghXNHsUL59DekXSHRJGi5lx2nYB8AAAEUAAAADHJzYS1zaGEyLTUxMgAAAQCap4+EcXiS6p5KMt4kB2XsDWAdz9Ww/xW6Ny35LrXKyH5vMZWSsL4Xg7H6KxdPDHKJTn80ST7+kMzkJVakw5zUfLjq1tbM2ieVH8GUD/hWaiyF0ug0Lls5K26kk4SNYezkrd2gEhrMSg430IFJgZEWUsdWNijAg5g7FnmY7MOs0ELY9xi4xaqAzpwTNbzDpisN6hWCrCFJs8YHKJmTVfyMGUJJxji4OwPtge/2N14cGh8IlFNqaw+fwEuiXElg549jBywcqUbESnCbYmVKQzvjNDy2eq9nPjhdd1RaPKP0oC5U8YgIIGKVTSeus+g+YSG8jViFst4HkiZkkHZKVsbd ossh-host diff --git a/keys/ossh/ossh-host-rsa-key.der b/keys/ossh/ossh-host-rsa-key.der new file mode 100644 index 0000000000000000000000000000000000000000..11c6d34d04dae6894f66d20a3ebb9e6248222e3c GIT binary patch literal 1192 zcmV;Z1Xueof&`=j0RRGm0RaH=0)&7YZz-eROH8{Xn!=4fdK&ZQ{uxsol*=M})I8PM ztw;$_q2}c8twuz^RD$Z)sn`)wRMq0$y>NgXiwttl1&h4EW|CA8MvK#@$|1aVV4ZAZ zldjvq5gk3?nEX73_jX-sUcVGm+V$E(` zAIeizof8mlsOcUy$l=xE@!HnfEU(F%_xkK!7t)%%Il-sUOI`F$JR-h}WutCtjij(~ z?=XhDAPK_dhs)UxT0J%*pS5l{#QS0k4$oKs5>ev;){;NP-b);grBvI2_8`@f77y;J z<^qiZckW65&x_(nvKO%e0|5X50)hbn0Gt{%pKy}Cm_5z%TaUKJp;p*xL_-cr1&bXD z{$4|4PDzh-G}QL#xoAWyTr+nM&nThB1~`3 z>%)H|$YGRQ`7~Z1l}GFO$B2kP+Ir;Jo5_%2fBnt}v^>YdeL%T4L0DVb_q*KG=|$sM zjHgY>#mwU!hzLey5pg5J<*A{C^#Xx`0Qui(Uy3$UXlOwlF`q|u00ShR#Tsd(Bnm<2 z&M_q;_xvq>8noIRAf+yaqkNnhLvUqUoy1tXz-H%o4)$H`M=(FmL`wzI_{F5!C7WZv zd3JbI087A{bN9B^&cc&83w5{pM+>4-B5}kDNEv4o11fjKC-;|fKOol&tAQ*RBLabe z0Qd2~6^@&Mav3~S^2qr)31t5^;i69+vFz2XP16$n>ZJ~@8JO*5t(6$6;K?50Jt}pd zHztYgCYYyX;E_-qB}l+6LeOneYz=)NA3G4Z%O7*nSdp z{h`2~T3G|r7SM6vpM`oIhmH+w?w4!&)>CnvjKr^e__S3=EQ_sdzl8B}lr|q(a>lZ0 z0)c>Wut3l^!-W zTX)en>1kt+Sz|YDOEAmLT~lc-x`YGyAYN z?24_S$$eFd-mT@251lv>jyXiK7fGPEDBWxRo%hn~iQcK|+}7%;8aG&Co}YuE_xRgp GdY#z-I8hn^ literal 0 HcmV?d00001 diff --git a/keys/ossh/ossh-host-rsa-key.pub b/keys/ossh/ossh-host-rsa-key.pub new file mode 100644 index 000000000..09de25f01 --- /dev/null +++ b/keys/ossh/ossh-host-rsa-key.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDyAoSAG28po95LTLsjmsKNPnoa8+b+GVMclMsie9Q81dmtSAlQoebk761GRMBUgurXqdgRUVTV4t29cIAdiwxyzwWLvMBmklQQRovTp8ohvHZgnWxjlO47ek3UquJgAwGwS2n+toippi9982iZ/memPnsEQJqEXUG4LMVFGI1AdYV3YAS/Ys1uXB/KU1WdExBuqOkeNsjh1eLx2tbaLK/Jm/f67F4X0pq8OcGn0Etd9E08Ir6LZaNuao2ksHHvMIa7IAnC5YfL2Q5aPTYin7VuOMT7YgsOz1gAElHjAdaSP8XeSxyOpVTbgfYg1ZEWD+6p5gKNAnfuSf/Pi+JIshex ossh-host diff --git a/keys/ossh/ossh-user-ca b/keys/ossh/ossh-user-ca new file mode 100644 index 000000000..27b148da4 --- /dev/null +++ b/keys/ossh/ossh-user-ca @@ -0,0 +1,27 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn +NhAAAAAwEAAQAAAQEAuzxTE9caka/aBi/e/eViyT94cNflPHnR9bLtDEs7hVKv2D0RIY/M +ufbVzaqGTt12iOdJoWe1awwT0Stj51+s8gf2VXrA/YoFOGG1K9rU3ZlVuPFahG0E0uCCj4 +WP7pl9ed9ewF1Bc6bkUIq8svvUMu4CEJhQIO0jiAhscUJs/ZRWLlY32/y6v8fEsHa+bsjS +RB/nSbYVOJ3P/C9J8hRm0FIg4d98S/2xHSyAZcrqoX3fe7lbwethP3/5+0aAzDqM+K8/A6 +PY4BtDskm+NBNkpFLSCNisyQQKuH7M//3vdKBJ/W9TVUjHJfEgDshfazBrkiSFpRSU0+K6 +TnLQJ5DhLwAAA8jyTPoW8kz6FgAAAAdzc2gtcnNhAAABAQC7PFMT1xqRr9oGL9795WLJP3 +hw1+U8edH1su0MSzuFUq/YPREhj8y59tXNqoZO3XaI50mhZ7VrDBPRK2PnX6zyB/ZVesD9 +igU4YbUr2tTdmVW48VqEbQTS4IKPhY/umX15317AXUFzpuRQiryy+9Qy7gIQmFAg7SOICG +xxQmz9lFYuVjfb/Lq/x8Swdr5uyNJEH+dJthU4nc/8L0nyFGbQUiDh33xL/bEdLIBlyuqh +fd97uVvB62E/f/n7RoDMOoz4rz8Do9jgG0OySb40E2SkUtII2KzJBAq4fsz//e90oEn9b1 +NVSMcl8SAOyF9rMGuSJIWlFJTT4rpOctAnkOEvAAAAAwEAAQAAAQBXT6UqDlfMciWIVxB0 +c2Biml9ut66lSeV33s0du4NxA4MOokN31BfS7GD9PDTPWASriLdUNglqdsJS+xnmTj5Wyx +G7ALg4QwF2LlIOtkgnYhaQXLQqJrBmKd2YUd8xk4/oohoMODCKXtv/FwMxr69fZI+/71cG +U68LA2qWUjSC9rnpRCP20R3nMslfoSafPSjp5V4Y9PfUNLci3S6OV4igwYwEN7Z4rb5OrA +kcSSE9HqZpXB0dDZMM2Br1wcMOe4xTkMGP+geuK51UhiRKyVsnsxObpVOD4NW/46HIpCUJ +d+K0zjKoghnxY94YMV8AeYBB3Ujp+W6yQnDI2LK84nJhAAAAgDQExDTnyg6QCiMlbQ3E5T +/r0532lNkUCtt896CiaOCbQ/o6/WtUw+D6okFC2IFVLDE8yt5sM4xW4Bj+S5T1dUQRenU+ +FR3FKEKUyDuYdHf77sxUYROGvO/hdwfbEMYzWWpbCeTt9pFTdkqcNyRfMxkbzyeaTcX+8C +oNNh6UuxWZAAAAgQDk6GvfMgRelzzdctCej9E8BgSg2uokjYxBbOtMZT3Lfsc50dT+yNRu +8UezQ2zcL572cSchbrZwGBL8TQ8POyoWYOerZSgr3twUvIUGD27u9pmiJ/H0To/GapEveX +oQo/Q6RWlX/oKzCmdZxThDMiIbXnEoWNwKteNI2ivg3mSZ/wAAAIEA0WVLR4rXebeRm7Ro +w10p7fRWzMinIFjOECI/qh9P9calsXIIpEJ3WJnLBQme4zo1ZSrAAsceH3+sQNq3Q9yENs +Ua7HKeKDlrHW9RkQZL73ES/9LjOeekMWaJI8J1lRp/HFHpjkkJuZOaVMqEpjjEUwFNS90Z +5n1QpXzgO0qA2NEAAAAMb3NzaC11c2VyLWNhAQIDBAUGBw== +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/ossh/ossh-user-ca.pub b/keys/ossh/ossh-user-ca.pub new file mode 100644 index 000000000..d46dff7a7 --- /dev/null +++ b/keys/ossh/ossh-user-ca.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7PFMT1xqRr9oGL9795WLJP3hw1+U8edH1su0MSzuFUq/YPREhj8y59tXNqoZO3XaI50mhZ7VrDBPRK2PnX6zyB/ZVesD9igU4YbUr2tTdmVW48VqEbQTS4IKPhY/umX15317AXUFzpuRQiryy+9Qy7gIQmFAg7SOICGxxQmz9lFYuVjfb/Lq/x8Swdr5uyNJEH+dJthU4nc/8L0nyFGbQUiDh33xL/bEdLIBlyuqhfd97uVvB62E/f/n7RoDMOoz4rz8Do9jgG0OySb40E2SkUtII2KzJBAq4fsz//e90oEn9b1NVSMcl8SAOyF9rMGuSJIWlFJTT4rpOctAnkOEv ossh-user-ca diff --git a/keys/ossh/ossh-user-rsa-key b/keys/ossh/ossh-user-rsa-key new file mode 100644 index 000000000..9a9219972 --- /dev/null +++ b/keys/ossh/ossh-user-rsa-key @@ -0,0 +1,27 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn +NhAAAAAwEAAQAAAQEA27IROonmIw0jmkVZDgYirvIAWFaCsQHNKh/HBTfiyjX11ukHWX8H +8Geq03LhZPI4DFkTEW0IbSzm35UUgdajEQFGyS/XfOST+UiV6ytrZSKBdqwk7qwZ5LK1Kv +o84RCAEFYWdv1G5+wTHxZSp42TIbatjz+lvvtgq/WnxGPNa61XwkZqFTwWwAZa0ts6200K +eQZER3+71J0i6ULaHevmP5nlGPQAGi6MD0Q+0ygbgzUWjUCMUlYdBkzRsMCCLpzExVHR11 +IMY3Ar0ekXs/ktoV0LAa8GOxdeZhALcIdfRve1FTFdje8busxqLE7wTyFfr1fwEIKS1vun +NlcaqoUXYQAAA8hHUV4QR1FeEAAAAAdzc2gtcnNhAAABAQDbshE6ieYjDSOaRVkOBiKu8g +BYVoKxAc0qH8cFN+LKNfXW6QdZfwfwZ6rTcuFk8jgMWRMRbQhtLObflRSB1qMRAUbJL9d8 +5JP5SJXrK2tlIoF2rCTurBnksrUq+jzhEIAQVhZ2/Ubn7BMfFlKnjZMhtq2PP6W++2Cr9a +fEY81rrVfCRmoVPBbABlrS2zrbTQp5BkRHf7vUnSLpQtod6+Y/meUY9AAaLowPRD7TKBuD +NRaNQIxSVh0GTNGwwIIunMTFUdHXUgxjcCvR6Rez+S2hXQsBrwY7F15mEAtwh19G97UVMV +2N7xu6zGosTvBPIV+vV/AQgpLW+6c2VxqqhRdhAAAAAwEAAQAAAQEAnW5d4Df/PXDl6V/2 +cBE/e2QZkTP1FUi4L45z/K5Oix7d2zJDvQNFuiVfiYSSNddJCLgv7NXYKvv5OZDIED6xvb +bPdc4dufbGfuVf4RTNMbznTxYOpSWYrITPREvrolBDpPE7yeq3i+xky2sgscbG/2q7tqIi +cNgML02q+hWWl6hcu86DVCxWm/CE9XzOx3vZHd7OE7s1z8umPBe4W2Jzw6uSNMk2xzKbw/ +u44j/Yi3d9aK6y8PEoVDNLX6AEF2rvL38D93HrKyw/iN9zzfq+2zzJLooxRFEsPn23WV7J +Sg5qX5UFUzYPwEgTETU01xxFVokEbbYyV35yT8/Si4H2kQAAAIEAm2YLaAaz4FMvulihM1 +x5LlQghCnBo5GeNmSqR0354XRX+AQrwV8GYZ/kJG1DUQYRyz0isTvctgIb5avSzhEQ4/iA +tjFEjjldL7PGa+1yVF1+jA75z3lyTKhaSmn6NztnWeiy9K4ANEmFlDaHYDrhElGY+rXYxh +ELsKOg85rQysUAAACBAPe4HTqG2F9Jx+ahtEXizzKRUKQ97n4u7j7UyVzol8jANlf5PJe4 +x8m4n16u6hJlN6mBIGE00spEmdfw83IUgfwQhnPEnmiB2GhbN83oc44OeE8fhYAMYvjo2x +tKiu1QywS0f1tLa9jCwveyZcTHTObyQzM/WYFTPG2gzQktnSO1AAAAgQDjCiNCeos7nIPz +U/VupgYfgFtnfuJk0ZsJsahDNUI9gXxX8kurS8IobcrOFnJy9Z5w9HILxf66r3Nt0ebp3v +zAKBQgUKYNfCEVcpDtRWk38tAgYM45iCCoXXGFwNtIU7TF+Zi3WuCGULuSI96B5ssAKUy8 +pkdWjI6eWKe/YxkIfQAAAApvc3NoLWFkbWluAQIDBAUGBw== +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/ossh/ossh-user-rsa-key-cert.pub b/keys/ossh/ossh-user-rsa-key-cert.pub new file mode 100644 index 000000000..f78c364b9 --- /dev/null +++ b/keys/ossh/ossh-user-rsa-key-cert.pub @@ -0,0 +1 @@ +ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgEFTKHk+oiRZityF/O7snclyrpHVdbdy7gIJvVuRUrAwAAAADAQABAAABAQDbshE6ieYjDSOaRVkOBiKu8gBYVoKxAc0qH8cFN+LKNfXW6QdZfwfwZ6rTcuFk8jgMWRMRbQhtLObflRSB1qMRAUbJL9d85JP5SJXrK2tlIoF2rCTurBnksrUq+jzhEIAQVhZ2/Ubn7BMfFlKnjZMhtq2PP6W++2Cr9afEY81rrVfCRmoVPBbABlrS2zrbTQp5BkRHf7vUnSLpQtod6+Y/meUY9AAaLowPRD7TKBuDNRaNQIxSVh0GTNGwwIIunMTFUdHXUgxjcCvR6Rez+S2hXQsBrwY7F15mEAtwh19G97UVMV2N7xu6zGosTvBPIV+vV/AQgpLW+6c2VxqqhRdhAAAAAAAAAAAAAAABAAAACm9zc2gtYWRtaW4AAAAOAAAACm9zc2gtYWRtaW4AAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAARcAAAAHc3NoLXJzYQAAAAMBAAEAAAEBALs8UxPXGpGv2gYv3v3lYsk/eHDX5Tx50fWy7QxLO4VSr9g9ESGPzLn21c2qhk7ddojnSaFntWsME9ErY+dfrPIH9lV6wP2KBThhtSva1N2ZVbjxWoRtBNLggo+Fj+6ZfXnfXsBdQXOm5FCKvLL71DLuAhCYUCDtI4gIbHFCbP2UVi5WN9v8ur/HxLB2vm7I0kQf50m2FTidz/wvSfIUZtBSIOHffEv9sR0sgGXK6qF933u5W8HrYT9/+ftGgMw6jPivPwOj2OAbQ7JJvjQTZKRS0gjYrMkECrh+zP/973SgSf1vU1VIxyXxIA7IX2swa5IkhaUUlNPiuk5y0CeQ4S8AAAEUAAAADHJzYS1zaGEyLTUxMgAAAQB+hYU1xZCyJ4Rk4Vef0F04WxBo+aZm123hsYeOwPQGioN8vC3+bbuGG+g3YlC39oowvsdNxUVN77Uj1q71KXCis98j/PGxJJ2OnumpTnfrkyKpbBEYXqE4V/Ec1AugO+BPVDus6J97WAkcx5b93a2+IXfYfTAKtcGQLfHu346VKbTUQAMBkECqoD5k1Ir0Ou4tdE3e+mkDYRVTKbFKW6ybgfamvxRhDR3wXU+POk0x5IXrf4Jb4ozWf3XbruQoPVXBwCLY/aR0Jmdr4KIVDzIPg27ysXPC6olfcMyy82jY+jRCmKeogqd8bVbz98IYA5TfZ1cf+gbH2PNj08AadiQe ossh-admin diff --git a/keys/ossh/ossh-user-rsa-key.pub b/keys/ossh/ossh-user-rsa-key.pub new file mode 100644 index 000000000..3fa7b1e61 --- /dev/null +++ b/keys/ossh/ossh-user-rsa-key.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDbshE6ieYjDSOaRVkOBiKu8gBYVoKxAc0qH8cFN+LKNfXW6QdZfwfwZ6rTcuFk8jgMWRMRbQhtLObflRSB1qMRAUbJL9d85JP5SJXrK2tlIoF2rCTurBnksrUq+jzhEIAQVhZ2/Ubn7BMfFlKnjZMhtq2PP6W++2Cr9afEY81rrVfCRmoVPBbABlrS2zrbTQp5BkRHf7vUnSLpQtod6+Y/meUY9AAaLowPRD7TKBuDNRaNQIxSVh0GTNGwwIIunMTFUdHXUgxjcCvR6Rez+S2hXQsBrwY7F15mEAtwh19G97UVMV2N7xu6zGosTvBPIV+vV/AQgpLW+6c2VxqqhRdh ossh-admin diff --git a/scripts/include.am b/scripts/include.am index 02f4d943f..8ddab2fc6 100644 --- a/scripts/include.am +++ b/scripts/include.am @@ -11,4 +11,11 @@ if BUILD_SCP dist_noinst_SCRIPTS+= scripts/scp.test endif +if BUILD_OPENSSH_CERTS +if BUILD_SHELL +# This test will hang if wolfSSH isn't built with shell support. +dist_noinst_SCRIPTS+= scripts/ossh-certs.test +endif BUILD_SHELL +endif BUILD_OPENSSH_CERTS + dist_noinst_SCRIPTS+= scripts/external.test diff --git a/scripts/ossh-certs-setup.sh b/scripts/ossh-certs-setup.sh new file mode 100755 index 000000000..e38a8287e --- /dev/null +++ b/scripts/ossh-certs-setup.sh @@ -0,0 +1,141 @@ +#!/bin/bash + +# +# This script can be used to regenerate the keys and OpenSSH-style certificates +# in the keys/ossh/ directory. This should never be necessary, as the valid +# (i.e. not intentionally expired) certs are made to never expire. +# + +script_dir=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) +ossh_dir=$script_dir/../keys/ossh + +which ssh-keygen +if [ $? != 0 ]; then + echo "ssh-keygen not found." + exit 1 +fi + +which openssl +if [ $? != 0 ]; then + echo "openssl not found." + exit 1 +fi + +# Host/server setup + +echo "Creating host CA RSA key pair..." +ssh-keygen -f $ossh_dir/ossh-host-ca \ + -N '' \ + -b 2048 \ + -t rsa \ + -C ossh-host-ca <<< y +if [ $? != 0 ]; then + echo "Failed to create host CA RSA key pair." + exit 1 +fi + +chmod 600 $ossh_dir/ossh-host-ca +if [ $? != 0 ]; then + echo "Failed to set permissions for host CA RSA private key." + exit 1 +fi + +echo "Creating host (server) RSA key pair..." +ssh-keygen -f $ossh_dir/ossh-host-rsa-key \ + -N '' \ + -b 2048 \ + -t rsa \ + -C ossh-host <<< y +if [ $? != 0 ]; then + echo "Failed to create host RSA key pair." + exit 1 +fi + +chmod 600 $ossh_dir/ossh-host-rsa-key +if [ $? != 0 ]; then + echo "Failed to set permissions for host RSA private key." +fi + +echo "Converting host RSA private key to PEM..." +ssh-keygen -p \ + -N '' \ + -m pem \ + -f $ossh_dir/ossh-host-rsa-key +if [ $? != 0 ]; then + echo "Failed to convert host RSA private key to PEM." + exit 1 +fi + +echo "Converting host RSA private key from PEM to DER..." +openssl rsa -in $ossh_dir/ossh-host-rsa-key \ + -outform DER \ + -out $ossh_dir/ossh-host-rsa-key.der +if [ $? != 0 ]; then + echo "Failed to convert host RSA private key from PEM to DER." + exit 1 +fi + +echo "Creating host RSA certificate, signed by host CA..." +ssh-keygen -s $ossh_dir/ossh-host-ca \ + -I localhost \ + -n localhost \ + -V always:forever \ + -h \ + $ossh_dir/ossh-host-rsa-key.pub +if [ $? != 0 ]; then + echo "Failed to create host RSA certificate." + exit 1 +fi + +# User/client setup + +echo "Creating user CA RSA key pair..." +ssh-keygen -f $ossh_dir/ossh-user-ca \ + -N '' \ + -b 2048 \ + -t rsa \ + -C ossh-user-ca <<< y +if [ $? != 0 ]; then + echo "Failed to create user CA RSA key pair." + exit 1 +fi + +chmod 600 $ossh_dir/ossh-user-ca +if [ $? != 0 ]; then + echo "Failed to set permissions for user CA RSA private key." +fi + +echo "Creating user (client) RSA key pair..." +ssh-keygen -f $ossh_dir/ossh-user-rsa-key \ + -N '' \ + -b 2048 \ + -t rsa \ + -C ossh-admin <<< y +if [ $? != 0 ]; then + echo "Failed to create user RSA key pair." + exit 1 +fi + +chmod 600 $ossh_dir/ossh-user-rsa-key +if [ $? != 0 ]; then + echo "Failed to set permissions for user RSA private key." + exit 1 +fi + +echo "Creating user RSA certificate, signed by user CA..." +ssh-keygen -s $ossh_dir/ossh-user-ca \ + -I ossh-admin \ + -n ossh-admin \ + -V always:forever \ + $ossh_dir/ossh-user-rsa-key.pub +if [ $? != 0 ]; then + echo "Failed to create user RSA certificate." + exit 1 +fi + +echo "Creating known_hosts file so client trusts host CA..." +echo "@cert-authority localhost $(cat $ossh_dir/ossh-host-ca.pub)" > $ossh_dir/known_hosts +if [ $? != 0 ]; then + echo "Failed to create known_hosts file." + exit 1 +fi diff --git a/scripts/ossh-certs.test b/scripts/ossh-certs.test new file mode 100755 index 000000000..1940688e3 --- /dev/null +++ b/scripts/ossh-certs.test @@ -0,0 +1,80 @@ +#!/bin/bash + +no_pid=-1 +server_pid=$no_pid +ready_file=`pwd`/wolfssh_sftp_ready$$ +counter=0 + +script_dir=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd) +ossh_dir=$script_dir/../keys/ossh +echoserver=$script_dir/../examples/echoserver/echoserver + +create_port() { + while [ ! -s "$ready_file" ] && [ "$counter" -lt 20 ]; do + echo "waiting for ready file..." + sleep 0.1 + counter=$((counter+ 1)) + done + + if [ -e $ready_file ]; then + echo "found ready file, starting client..." + + # get created port 0 ephemeral port + port=`cat $ready_file` + else + echo "NO ready file ending test..." + do_cleanup + exit 1 + fi +} + +remove_ready_file() { + if [ -e $ready_file ]; then + echo "removing existing ready file" + rm $ready_file + fi +} + +do_cleanup() { + echo "in cleanup" + + if [ $server_pid != $no_pid ]; then + echo "killing server" + kill -9 $server_pid + fi + remove_ready_file +} + +do_trap() { + echo "got trap" + do_cleanup + exit -1 +} + +trap do_trap INT TERM + +echo "Scenario: server presents cert trusted by client, client presents cert trusted by server." +$echoserver -1 \ + -R $ready_file \ + -C $ossh_dir/ossh-host-rsa-key-cert.pub \ + -T $ossh_dir/ossh-user-ca.pub \ + -k $ossh_dir/ossh-host-rsa-key.der \ + -j $ossh_dir/ossh-user-rsa-key-cert.pub &>/dev/null & +server_pid=$! +create_port +ssh -p $port \ + -i $ossh_dir/ossh-user-rsa-key \ + -o "BatchMode yes" \ + -o "UserKnownHostsFile=$ossh_dir/known_hosts" \ + ossh-admin@localhost exit 2>&1 | grep "wolfSSH Example Echo Server" +RESULT=$? +remove_ready_file +if [ $RESULT != 0 ]; then + echo "Scenario failed. $RESULT" + do_cleanup + exit 1 +else + echo "Scenario passed." +fi + +echo "All scenarios passed." diff --git a/src/include.am b/src/include.am index 8b70bdccd..84ca65156 100644 --- a/src/include.am +++ b/src/include.am @@ -38,3 +38,8 @@ endif if BUILD_CERTS src_libwolfssh_la_SOURCES += src/certman.c endif + +if BUILD_OPENSSH_CERTS +src_libwolfssh_la_SOURCES += src/ossh_certs.c +src_libwolfssh_la_SOURCES += src/list.c +endif diff --git a/src/internal.c b/src/internal.c index 13f5679d6..7fc02aaa2 100644 --- a/src/internal.c +++ b/src/internal.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #ifndef WOLFSSH_NO_DH #include @@ -133,6 +134,9 @@ Set when all ECDH algorithms are disabled. Set to disable use of all ECDH algorithms for key agreement. Setting this will force all ECDH key agreement algorithms off. + WOLFSSH_OSSH_CERTS + Adds support for using OpenSSH-style certificates for host and user auth. + See https://man.openbsd.org/ssh-keygen#CERTIFICATES. */ @@ -408,6 +412,15 @@ const char* GetErrorString(int err) case WS_CERT_KEY_SIZE_E: return "key size too small error"; + case WS_OSSH_CERT_PARSE_E: + return "error parsing OpenSSH cert"; + + case WS_OSSH_CERT_CA_E: + return "error processing OpenSSH cert CA"; + + case WS_OSSH_CERT_EXPIRED_E: + return "OpenSSH cert expired"; + default: return "Unknown error code"; } @@ -565,6 +578,15 @@ WOLFSSH_CTX* CtxInit(WOLFSSH_CTX* ctx, byte side, void* heap) if (ctx->certMan == NULL) return NULL; #endif /* WOLFSSH_CERTS */ +#ifdef WOLFSSH_OSSH_CERTS + ctx->osshCAKeys = ListNew(LIST_OSSH_CA_KEY, ctx->heap); + if (ctx->osshCAKeys == NULL) { + #ifdef WOLFSSH_CERTS + wolfSSH_CERTMAN_free(ctx->certMan); + #endif /* WOLFSSH_CERTS */ + return NULL; + } +#endif /* WOLFSSH_OSSH_CERTS */ ctx->windowSz = DEFAULT_WINDOW_SZ; ctx->maxPacketSz = DEFAULT_MAX_PACKET_SZ; @@ -588,6 +610,13 @@ void CtxResourceFree(WOLFSSH_CTX* ctx) WFREE(ctx->cert, ctx->heap, DYNTYPE_CERT); } #endif +#ifdef WOLFSSH_OSSH_CERTS + if (ctx->osshCertRaw != NULL) { + WFREE(ctx->osshCertRaw, ctx->heap, DYNTYPE_PUBKEY); + } + OsshCertFree(ctx->osshCert); + ListFree(ctx->osshCAKeys); +#endif /* WOLFSSH_OSSH_CERTS */ } @@ -776,6 +805,155 @@ union wolfSSH_key { #endif }; +#ifdef WOLFSSH_OSSH_CERTS + +/* + * Read the buffer in of size inSz and attempt to parse it as an OpenSSH-style + * certificate. If successful, it save the certificate in the ctx and use it to + * for authentication. Returns WS_SUCCESS on success and negative values on + * failure. + */ +static int CtxUseOsshCert(WOLFSSH_CTX* ctx, const byte* in, word32 inSz) +{ + int ret = WS_SUCCESS; + byte* certBuf = NULL; + word32 certBufSz; + const byte* certType; + word32 certTypeSz; + byte certId; + WOLFSSH_OSSH_CERT* osshCert = NULL; + void* heap; + + if (ctx == NULL || in == NULL || inSz == 0) { + ret = WS_BAD_ARGUMENT; + } + else { + heap = ctx->heap; + } + + if (ret == WS_SUCCESS) { + ret = wolfSSH_ReadKey_buffer(in, inSz, WOLFSSH_FORMAT_SSH, &certBuf, + &certBufSz, &certType, &certTypeSz, heap); + + if (ret == WS_SUCCESS) { + ret = ParseOsshCert(certBuf, certBufSz, &osshCert, ctx->side, heap); + + if (ret == WS_SUCCESS) { + certId = NameToId((const char*)certType, certTypeSz); + + if (certId == osshCert->type) { + if (ctx->osshCert != NULL) { + OsshCertFree(ctx->osshCert); + } + if (ctx->osshCertRaw != NULL) { + WFREE(ctx->osshCertRaw, heap, DYNTYPE_PUBKEY); + } + + ctx->osshCert = osshCert; + /* + * We save a pointer to the raw cert buffer, as we'll need + * to send it in full for authentication later. + */ + ctx->osshCertRaw = certBuf; + ctx->osshCertRawSz = certBufSz; + ctx->useOsshCert = 1; + } + else { + WLOG(WS_LOG_ERROR, "Encoded OpenSSH cert type %u differs " + "from type in file %u.", osshCert->type, + certId); + ret = WS_FATAL_ERROR; + } + } + else { + WLOG(WS_LOG_ERROR, "Failed to parse OpenSSH-style cert."); + } + } + else { + WLOG(WS_LOG_ERROR, "Failed to read OpenSSH-style cert from " + "buffer."); + } + } + + if (ret != WS_SUCCESS) { + if (certBuf != NULL) { + WFREE(certBuf, heap, DYNTYPE_PUBKEY); + } + if (osshCert != NULL) { + OsshCertFree(osshCert); + } + } + + return ret; +} + +/* + * Add a trusted CA public key for validating OpenSSH-style certificates. The + * key is read from the buffer in of size inSz. If successful, the key is added + * to a list owned by the ctx. The keys in this list are used to verify if a + * cert presented by the other side of the connection can be trusted. Returns + * WS_SUCCESS on success and negative values on failure. + */ +static int CtxAddOsshCaKey(WOLFSSH_CTX* ctx, const byte* in, word32 inSz) +{ + int ret = WS_SUCCESS; + byte* rawKey = NULL; + word32 rawKeySz; + const byte* keyType; + word32 keyTypeSz; + WOLFSSH_OSSH_CA_KEY* osshCaKey = NULL; + void* heap; + + if (ctx == NULL || in == NULL || inSz == 0) { + ret = WS_BAD_ARGUMENT; + } + else { + heap = ctx->heap; + } + + if (ret == WS_SUCCESS) { + ret = wolfSSH_ReadKey_buffer(in, inSz, WOLFSSH_FORMAT_SSH, &rawKey, + &rawKeySz, &keyType, &keyTypeSz, heap); + + if (ret == WS_SUCCESS) { + osshCaKey = OsshCaKeyNew(ctx->heap); + + if (osshCaKey != NULL) { + ret = OsshCaKeyInit(osshCaKey, rawKey, rawKeySz); + + if (ret == WS_SUCCESS) { + ret = ListAdd(ctx->osshCAKeys, osshCaKey); + + if (ret != WS_SUCCESS) { + WLOG(WS_LOG_ERROR, "Failed add CA key to trusted " + "list."); + } + } + else { + WLOG(WS_LOG_ERROR, "Failed to initialize CA key."); + } + } + else { + ret = WS_MEMORY_E; + } + } + else { + WLOG(WS_LOG_ERROR, "Failed to read CA key from buffer."); + } + } + + if (rawKey != NULL) { + WFREE(rawKey, heap, DYNTYPE_PUBKEY); + } + if (ret != WS_SUCCESS) { + OsshCaKeyFree(osshCaKey); + } + + return ret; +} + +#endif /* WOLFSSH_OSSH_CERTS */ + int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx, const byte* in, word32 inSz, int format, int type) @@ -784,8 +962,8 @@ int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx, int wcType; int ret; void* heap = NULL; - byte* der; - word32 derSz, scratch = 0; + byte* der = NULL; + word32 derSz = 0, scratch = 0; union wolfSSH_key *key_ptr = NULL; (void)dynamicType; @@ -795,55 +973,85 @@ int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx, return WS_BAD_ARGUMENT; if (format != WOLFSSH_FORMAT_ASN1 && format != WOLFSSH_FORMAT_PEM && - format != WOLFSSH_FORMAT_RAW) + format != WOLFSSH_FORMAT_RAW && format != WOLFSSH_FORMAT_SSH) { return WS_BAD_FILETYPE_E; - - if (type == BUFTYPE_CA) { - dynamicType = DYNTYPE_CA; - wcType = CA_TYPE; - } - else if (type == BUFTYPE_CERT) { - dynamicType = DYNTYPE_CERT; - wcType = CERT_TYPE; } - else if (type == BUFTYPE_PRIVKEY) { - dynamicType = DYNTYPE_PRIVKEY; - wcType = PRIVATEKEY_TYPE; + + switch (type) { + case BUFTYPE_CA: + dynamicType = DYNTYPE_CA; + wcType = CA_TYPE; + break; + + case BUFTYPE_PRIVKEY: + dynamicType = DYNTYPE_PRIVKEY; + wcType = PRIVATEKEY_TYPE; + break; + + #ifdef WOLFSSH_CERTS + case BUFTYPE_CERT: + dynamicType = DYNTYPE_CERT; + wcType = CERT_TYPE; + break; + #endif /* WOLFSSH_CERTS */ + + #ifdef WOLFSSH_OSSH_CERTS + case BUFTYPE_OSSH_CERT: + case BUFTYPE_OSSH_CA_KEY: + /* Nothing to do for these types. */ + break; + #endif /* WOLFSSH_OSSH_CERTS */ + + default: + WLOG(WS_LOG_ERROR, "Bad type: %d.", type); + return WS_BAD_ARGUMENT; } - else - return WS_BAD_ARGUMENT; heap = ctx->heap; - if (format == WOLFSSH_FORMAT_ASN1 || format == WOLFSSH_FORMAT_RAW) { - if (in[0] != 0x30) - return WS_BAD_FILETYPE_E; - der = (byte*)WMALLOC(inSz, heap, dynamicType); - if (der == NULL) - return WS_MEMORY_E; - WMEMCPY(der, in, inSz); - derSz = inSz; - } + switch (format) { + case WOLFSSH_FORMAT_ASN1: + case WOLFSSH_FORMAT_RAW: + if (in[0] != 0x30) + return WS_BAD_FILETYPE_E; + der = (byte*)WMALLOC(inSz, heap, dynamicType); + if (der == NULL) + return WS_MEMORY_E; + WMEMCPY(der, in, inSz); + derSz = inSz; + break; + #ifdef WOLFSSH_CERTS - else if (format == WOLFSSH_FORMAT_PEM) { - /* The der size will be smaller than the pem size. */ - der = (byte*)WMALLOC(inSz, heap, dynamicType); - if (der == NULL) - return WS_MEMORY_E; + case WOLFSSH_FORMAT_PEM: + /* The der size will be smaller than the pem size. */ + der = (byte*)WMALLOC(inSz, heap, dynamicType); + if (der == NULL) + return WS_MEMORY_E; - ret = wc_CertPemToDer(in, inSz, der, inSz, wcType); - if (ret < 0) { - WFREE(der, heap, dynamicType); - return WS_BAD_FILE_E; - } - derSz = (word32)ret; - } + ret = wc_CertPemToDer(in, inSz, der, inSz, wcType); + if (ret < 0) { + WFREE(der, heap, dynamicType); + return WS_BAD_FILE_E; + } + derSz = (word32)ret; + break; #endif /* WOLFSSH_CERTS */ - else { - return WS_UNIMPLEMENTED_E; - } - /* Maybe decrypt */ + #ifdef WOLFSSH_OSSH_CERTS + case WOLFSSH_FORMAT_SSH: + if (type != BUFTYPE_OSSH_CERT && type != BUFTYPE_OSSH_CA_KEY) { + WLOG(WS_LOG_ERROR, "Buffer format WOLFSSH_FORMAT_SSH requires " + "buffer type to be BUFTYPE_OSSH_CERT or " + "BUFTYPE_OSSH_CA_KEY."); + return WS_BAD_ARGUMENT; + } + break; + #endif /* WOLFSSH_OSSH_CERTS */ + + default: + WLOG(WS_LOG_ERROR, "Bad buffer format: %d.", format); + return WS_BAD_ARGUMENT; + } if (type == BUFTYPE_PRIVKEY) { if (ctx->privateKey) { @@ -854,7 +1062,7 @@ int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx, ctx->privateKeySz = derSz; ctx->useEcc = 0; } - #ifdef WOLFSSH_CERTS +#ifdef WOLFSSH_CERTS else if (type == BUFTYPE_CERT) { if (ctx->cert != NULL) WFREE(ctx->cert, heap, dynamicType); @@ -876,9 +1084,25 @@ int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx, goto end; } } - #endif /* WOLFSSH_CERTS */ +#endif /* WOLFSSH_CERTS */ +#ifdef WOLFSSH_OSSH_CERTS + else if (type == BUFTYPE_OSSH_CERT) { + ret = CtxUseOsshCert(ctx, in, inSz); + if (ret != WS_SUCCESS) { + return ret; + } + } + else if (type == BUFTYPE_OSSH_CA_KEY) { + ret = CtxAddOsshCaKey(ctx, in, inSz); + if (ret != WS_SUCCESS) { + return ret; + } + } +#endif /* WOLFSSH_OSSH_CERTS */ else { - WFREE(der, heap, dynamicType); + if (der != NULL) { + WFREE(der, heap, dynamicType); + } return WS_UNIMPLEMENTED_E; } @@ -1210,7 +1434,10 @@ static const NameIdPair NameIdMap[] = { /* Public Key IDs */ #ifndef WOLFSSH_NO_SSH_RSA_SHA1 { ID_SSH_RSA, "ssh-rsa" }, -#endif +#ifdef WOLFSSH_OSSH_CERTS + { ID_OSSH_CERT_RSA, "ssh-rsa-cert-v01@openssh.com" }, +#endif /* WOLFSSH_OSSH_CERTS */ +#endif /* !WOLFSSH_NO_SSH_RSA_SHA1 */ #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256 { ID_ECDSA_SHA2_NISTP256, "ecdsa-sha2-nistp256" }, #endif @@ -1258,6 +1485,12 @@ static const NameIdPair NameIdMap[] = { { ID_GLOBREQ_TCPIP_FWD, "tcpip-forward" }, { ID_GLOBREQ_TCPIP_FWD_CANCEL, "cancel-tcpip-forward" }, #endif /* WOLFSSH_FWD */ +#ifndef WOLFSSH_NO_SSH_RSA_SHA2_256 + { ID_RSA_SHA2_256, "rsa-sha2-256" }, +#endif /* !WOLFSSH_NO_SSH_RSA_SHA2_256 */ +#ifndef WOLFSSH_NO_SSH_RSA_SHA2_512 + { ID_RSA_SHA2_512, "rsa-sha2-512" }, +#endif /* !WOLFSSH_NO_SSH_RSA_SHA2_512 */ }; @@ -1946,6 +2179,20 @@ int GetUint32(word32* v, const byte* buf, word32 len, word32* idx) } +int GetUint64(w64wrapper* v, const byte* buf, word32 len, word32* idx) +{ + int result = WS_BUFFER_E; + + if (*idx < len && UINT64_SZ <= len - *idx) { + ato64(buf + *idx, v); + *idx += UINT64_SZ; + result = WS_SUCCESS; + } + + return result; +} + + int GetSize(word32* v, const byte* buf, word32 len, word32* idx) { int result; @@ -2246,6 +2493,11 @@ static const byte cannedKeyAlgoClient[] = { sizeof(cannedKeyAlgoX509Ecc521); #endif #endif /* WOLFSSH_CERTS */ +#ifdef WOLFSSH_OSSH_CERTS + static const byte cannedKeyAlgoOsshCertRsa[] = {ID_OSSH_CERT_RSA}; + static const word32 cannedKeyAlgoOsshCertRsaSz = + sizeof(cannedKeyAlgoOsshCertRsaSz); +#endif /* WOLFSSH_OSSH_CERTS */ static const byte cannedKexAlgo[] = { @@ -2423,8 +2675,11 @@ static INLINE enum wc_HashType HashForId(byte id) #ifdef WOLFSSH_CERTS case ID_X509V3_SSH_RSA: #endif + #ifdef WOLFSSH_OSSH_CERTS + case ID_OSSH_CERT_RSA: + #endif /* WOLFSSH_OSSH_CERTS */ return WC_HASH_TYPE_SHA; -#endif +#endif /* !WOLFSSH_NO_SSH_RSA_SHA1 */ /* SHA2-256 */ #ifndef WOLFSSH_NO_DH_GEX_SHA256 @@ -2561,6 +2816,9 @@ static INLINE byte SigTypeForId(byte id) #ifdef WOLFSSH_CERTS case ID_X509V3_SSH_RSA: #endif + #ifdef WOLFSSH_OSSH_CERTS + case ID_OSSH_CERT_RSA: + #endif /* WOLFSSH_OSSH_CERTS */ case ID_SSH_RSA: return ID_SSH_RSA; #endif @@ -2721,16 +2979,22 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) else { #ifndef WOLFSSH_NO_SSH_RSA_SHA1 if (ssh->ctx->useCert) { - #ifdef WOLFSSH_CERTS + #ifdef WOLFSSH_CERTS cannedKeyAlgo = cannedKeyAlgoX509Rsa; cannedKeyAlgoSz = cannedKeyAlgoX509RsaSz; - #endif + #endif + } + #ifdef WOLFSSH_OSSH_CERTS + else if (ssh->ctx->useOsshCert) { + cannedKeyAlgo = cannedKeyAlgoOsshCertRsa; + cannedKeyAlgoSz = cannedKeyAlgoOsshCertRsaSz; } + #endif /* WOLFSSH_OSSH_CERTS */ else { cannedKeyAlgo = cannedKeyAlgoRsa; cannedKeyAlgoSz = cannedKeyAlgoRsaSz; } - #endif + #endif /* !WOLFSSH_NO_SSH_RSA_SHA1 */ } } else { @@ -4638,6 +4902,7 @@ static int DoUserAuthRequestPassword(WOLFSSH* ssh, WS_UserAuthData* authData, return ret; } + #ifndef WOLFSSH_NO_RSA /* Utility for DoUserAuthRequestPublicKey() */ /* returns negative for error, positive is size of digest. */ @@ -4710,8 +4975,9 @@ static int DoUserAuthRequestRsa(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, } } - if (ret == WS_SUCCESS) + if (ret == WS_SUCCESS) { ret = GetSize(&eSz, pk->publicKey, pk->publicKeySz, &i); + } if (ret == WS_SUCCESS) { e = pk->publicKey + i; @@ -4972,6 +5238,99 @@ static int DoUserAuthRequestRsaCert(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, return ret; } #endif /* WOLFSSH_CERTS */ + +#ifdef WOLFSSH_OSSH_CERTS + +static int CheckOsshCertSigType(const WS_UserAuthData_PublicKey* pk, + const byte* sigKeyType, word32 sigKeyTypeSz); + +/* + * Check that the signature on a received OpenSSH-style certificate is valid + * (i.e. that the sender actually possesses the corresponding private key). + * The hash of size hashSz gets DER-encoded and compared against the signature, + * which is decrypted with the public key held in the cert. If these match, + * the signature is valid and WS_SUCCESS is returned. On failure, returns + * negative values. + */ +static int CheckOsshCertSignature(WOLFSSH* ssh, byte pkTypeId, + WOLFSSH_OSSH_CERT* cert, WS_UserAuthData_PublicKey* pk, byte hashId, + byte* hash, word32 hashSz) +{ + int ret = WS_SUCCESS; + byte* keyType; + word32 keyTypeSz; + byte* sig; + word32 sigSz; + word32 idx = 0; + byte derHash[MAX_ENCODED_SIG_SZ]; + word32 derHashSz; + enum wc_SignatureType sigType = WC_SIGNATURE_TYPE_NONE; + + if (ssh == NULL || cert == NULL || cert->pubKey == NULL || pk == NULL || + hash == NULL || hashSz == 0) { + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { + ret = GetStringRef(&keyTypeSz, &keyType, (byte*)pk->signature, + pk->signatureSz, &idx); + + if (ret == WS_SUCCESS) { + ret = CheckOsshCertSigType(pk, keyType, keyTypeSz); + + if (ret == WS_SUCCESS) { + ret = GetStringRef(&sigSz, &sig, (byte*)pk->signature, + pk->signatureSz, &idx); + + if (ret == WS_SUCCESS) { + derHashSz = wc_EncodeSignature(derHash, hash, hashSz, + wc_HashGetOID(hashId)); + + if (derHashSz > 0) { + switch (pkTypeId) { + case ID_OSSH_CERT_RSA: + sigType = WC_SIGNATURE_TYPE_RSA_W_ENC; + break; + default: + break; + } + if (sigType != WC_SIGNATURE_TYPE_NONE) { + ret = wc_SignatureVerifyHash(hashId, sigType, + derHash, derHashSz, sig, sigSz, + cert->pubKey, sizeof(RsaKey)); + + if (ret == 0) { + ret = WS_SUCCESS; + } + else { + WLOG(WS_LOG_ERROR, "Failed to verify " + "signature."); + } + } + else { + WLOG(WS_LOG_ERROR, "Failed to get signature type " + "for public key type %u.", pkTypeId); + } + } + else { + WLOG(WS_LOG_ERROR, "Failed to create encoded signature " + "to compare against received signature."); + ret = WS_FATAL_ERROR; + } + } + else { + WLOG(WS_LOG_ERROR, "Failed to get signature."); + } + } + } + else { + WLOG(WS_LOG_ERROR, "Failed to get key type for signature."); + } + } + + return ret; +} +#endif /* WOLFSSH_OSSH_CERTS */ #endif /* ! WOLFSSH_NO_RSA */ @@ -5375,6 +5734,75 @@ static int DoUserAuthRequestEccCert(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, #endif /* WOLFSSH_CERTS */ #endif /* ! WOLFSSH_NO_ECDSA */ +#ifdef WOLFSSH_OSSH_CERTS + +/* + * Parses and verifies a received OpenSSH-style cert. Verification consists + * of checking that the username matches a principal listed on the cert and that + * the CA public key is trusted. Returns WS_SUCCESS on success and negative + * values on failure. + * + * This function does NOT check the cert's signature. That is handled later by + * CheckOsshCertSignature. + */ +static int ParseAndVerifyOsshCert(WOLFSSH_CTX* ctx, WS_UserAuthData* authData, + WOLFSSH_OSSH_CERT** cert) +{ + int ret = WS_SUCCESS; + WS_UserAuthData_PublicKey* pk; + WOLFSSH_OSSH_CERT* certPtr; + + if (ctx == NULL || authData == NULL || cert == NULL || *cert != NULL) { + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { + pk = &authData->sf.publicKey; + ret = ParseOsshCert((byte*)pk->publicKey, pk->publicKeySz, cert, + WOLFSSH_ENDPOINT_CLIENT, ctx->heap); + if (ret == WS_SUCCESS) { + certPtr = *cert; + /* + * Per OpenSSH documentation: "As a special case, a + * zero-length "valid principals" field means the + * certificate is valid for any principal of the specified + * type." So, certPtr->principals == NULL isn't a failure condition. + */ + if (certPtr->principals != NULL && + !ListFind(certPtr->principals, authData->username, + authData->usernameSz)) { + WLOG(WS_LOG_ERROR, "User %s isn't listed as a valid " + "principal on this cert.", authData->username); + ret = WS_FATAL_ERROR; + } + } + else { + WLOG(WS_LOG_ERROR, "Failed to parse OpenSSH-style cert."); + } + } + if (ret == WS_SUCCESS) { + if (ctx->osshCAKeys == NULL) { + WLOG(WS_LOG_ERROR, "No CA keys for OpenSSH-style certs loaded. " + "Cannot trust this cert."); + ret = WS_FATAL_ERROR; + } + else { + /* Ensure the CA public key is one we trust. */ + if (!ListFind(ctx->osshCAKeys, certPtr->caKeyFingerprint, + sizeof(certPtr->caKeyFingerprint))) { + WLOG(WS_LOG_ERROR, "CA key for this cert isn't trusted."); + ret = WS_FATAL_ERROR; + } + else { + pk->isOsshCert = 1; + } + } + } + + return ret; +} + +#endif /* WOLFSSH_OSSH_CERTS */ #if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA) /* Utility for DoUserAuthRequest() */ @@ -5388,6 +5816,9 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, byte pkTypeId = ID_NONE; byte* pkOk = NULL; word32 pkOkSz = 0; +#ifdef WOLFSSH_OSSH_CERTS + WOLFSSH_OSSH_CERT* osshCert = NULL; +#endif /* WOLFSSH_OSSH_CERTS */ WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestPublicKey()"); @@ -5430,7 +5861,7 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, pkOk = (byte*)pk->publicKey; pkOkSz = pk->publicKeySz; - #ifdef WOLFSSH_CERTS + #ifdef WOLFSSH_CERTS if (pkTypeId == ID_X509V3_SSH_RSA || pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP256 || pkTypeId == ID_X509V3_ECDSA_SHA2_NISTP384 || @@ -5451,48 +5882,58 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, authFailure = 1; } } - #endif /* WOLFSSH_CERTS */ - - if (ret == WS_SUCCESS && !authFailure) { - if (pk->hasSignature) { - ret = GetSize(&pk->signatureSz, buf, len, &begin); - if (ret == WS_SUCCESS) { - pk->signature = buf + begin; - begin += pk->signatureSz; - } + else + #endif /* WOLFSSH_CERTS */ + #ifdef WOLFSSH_OSSH_CERTS + if (pkTypeId == ID_OSSH_CERT_RSA) { + ret = ParseAndVerifyOsshCert(ssh->ctx, authData, &osshCert); + if (ret != WS_SUCCESS) { + authFailure = 1; + ret = SendUserAuthFailure(ssh, 0); } - else { - pk->signature = NULL; - pk->signatureSz = 0; + } + #endif /* WOLFSSH_OSSH_CERTS */ + } + + if (ret == WS_SUCCESS && !authFailure) { + if (pk->hasSignature) { + ret = GetSize(&pk->signatureSz, buf, len, &begin); + if (ret == WS_SUCCESS) { + pk->signature = buf + begin; + begin += pk->signatureSz; } } + else { + pk->signature = NULL; + pk->signatureSz = 0; + } + } - if (ret == WS_SUCCESS && !authFailure) { - *idx = begin; + if (ret == WS_SUCCESS && !authFailure) { + *idx = begin; - if (ssh->ctx->userAuthCb != NULL) { - WLOG(WS_LOG_DEBUG, "DUARPK: Calling the userauth callback"); - ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PUBLICKEY, - authData, ssh->userAuthCtx); - WLOG(WS_LOG_DEBUG, "DUARPK: callback result = %d", ret); - if (ret == WOLFSSH_USERAUTH_SUCCESS) - ret = WS_SUCCESS; - else if (ret == WOLFSSH_USERAUTH_INVALID_PUBLICKEY) { - WLOG(WS_LOG_DEBUG, "DUARPK: client key rejected"); - ret = SendUserAuthFailure(ssh, 0); - authFailure = 1; - } - else { - ret = SendUserAuthFailure(ssh, 0); - authFailure = 1; - } + if (ssh->ctx->userAuthCb != NULL) { + WLOG(WS_LOG_DEBUG, "DUARPK: Calling the userauth callback"); + ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PUBLICKEY, + authData, ssh->userAuthCtx); + WLOG(WS_LOG_DEBUG, "DUARPK: callback result = %d", ret); + if (ret == WOLFSSH_USERAUTH_SUCCESS) + ret = WS_SUCCESS; + else if (ret == WOLFSSH_USERAUTH_INVALID_PUBLICKEY) { + WLOG(WS_LOG_DEBUG, "DUARPK: client key rejected"); + ret = SendUserAuthFailure(ssh, 0); + authFailure = 1; } else { - WLOG(WS_LOG_DEBUG, "DUARPK: no userauth callback set"); ret = SendUserAuthFailure(ssh, 0); authFailure = 1; } } + else { + WLOG(WS_LOG_DEBUG, "DUARPK: no userauth callback set"); + ret = SendUserAuthFailure(ssh, 0); + authFailure = 1; + } } if (ret == WS_SUCCESS && !authFailure) { @@ -5507,14 +5948,12 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, word32 digestSz = 0; enum wc_HashType hashId = WC_HASH_TYPE_SHA; - if (ret == WS_SUCCESS) { - hashId = HashForId(pkTypeId); - WMEMSET(digest, 0, sizeof(digest)); - ret = wc_HashGetDigestSize(hashId); - if (ret > 0) { - digestSz = ret; - ret = 0; - } + hashId = HashForId(pkTypeId); + WMEMSET(digest, 0, sizeof(digest)); + ret = wc_HashGetDigestSize(hashId); + if (ret > 0) { + digestSz = ret; + ret = 0; } if (ret == 0) @@ -5568,6 +6007,12 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, hashId, digest, digestSz); break; #endif + #ifdef WOLFSSH_OSSH_CERTS + case ID_OSSH_CERT_RSA: + ret = CheckOsshCertSignature(ssh, pkTypeId, osshCert, + pk, hashId, digest, digestSz); + break; + #endif /* WOLFSSH_OSSH_CERTS */ #endif #ifndef WOLFSSH_NO_ECDSA case ID_ECDSA_SHA2_NISTP256: @@ -5618,6 +6063,10 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, } } +#ifdef WOLFSSH_OSSH_CERTS + OsshCertFree(osshCert); +#endif /* WOLFSSH_OSSH_CERTS */ + WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestPublicKey(), ret = %d", ret); return ret; } @@ -7670,6 +8119,10 @@ static const char cannedKeyAlgoRsaNames[] = "ssh-rsa"; #ifdef WOLFSSH_CERTS static const char cannedKeyAlgoX509RsaNames[] = "x509v3-ssh-rsa"; #endif +#ifdef WOLFSSH_OSSH_CERTS +static const char osshRsaCertKeyAlgoName[] = "ssh-rsa-cert-v01@openssh.com"; +#endif /* WOLFSSH_OSSH_CERTS */ + #if !defined(WOLFSSH_NO_ECDSA) && !defined(WOLFSSH_NO_ECDH) static const char cannedKeyAlgoEcc256Names[] = "ecdsa-sha2-nistp256"; static const char cannedKeyAlgoEcc384Names[] = "ecdsa-sha2-nistp384"; @@ -7748,6 +8201,42 @@ static const word32 cannedKexAlgoNamesSz = sizeof(cannedKexAlgoNames) - 2; static const word32 cannedNoneNamesSz = sizeof(cannedNoneNames) - 1; +#ifdef WOLFSSH_OSSH_CERTS +/* + * Checks that the public key type from a signature on an OpenSSH-style cert + * matches the cert's stated public key type. Returns WS_SUCCESS on success and + * WS_FATAL_ERROR on failure. + */ +static int CheckOsshCertSigType(const WS_UserAuthData_PublicKey* pk, + const byte* sigKeyType, word32 sigKeyTypeSz) +{ + int ret = WS_FATAL_ERROR; + + if (pk == NULL || sigKeyType == NULL || sigKeyTypeSz == 0) { + ret = WS_BAD_ARGUMENT; + } + else { + /* + * Public key type ssh-rsa-cert-v01@openssh.com corresponds to + * signature type ssh-rsa. + */ + if (WSTRLEN(osshRsaCertKeyAlgoName) == pk->publicKeyTypeSz && + WMEMCMP(osshRsaCertKeyAlgoName, pk->publicKeyType, + pk->publicKeyTypeSz) == 0 && + WSTRLEN(cannedKeyAlgoRsaNames) == sigKeyTypeSz && + WMEMCMP(cannedKeyAlgoRsaNames, sigKeyType, sigKeyTypeSz) == 0) { + ret = WS_SUCCESS; + } + else { + WLOG(WS_LOG_ERROR, "Key type used to generate signature doesn't " + "match user's public key."); + } + } + + return ret; +} +#endif /* WOLFSSH_OSSH_CERTS */ + int SendKexInit(WOLFSSH* ssh) { byte* output = NULL; @@ -7829,6 +8318,12 @@ int SendKexInit(WOLFSSH* ssh) cannedKeyAlgoNamesSz = cannedKeyAlgoX509RsaNamesSz; #endif } + #ifdef WOLFSSH_OSSH_CERTS + else if (ssh->ctx->useOsshCert) { + cannedKeyAlgoNames = osshRsaCertKeyAlgoName; + cannedKeyAlgoNamesSz = sizeof(osshRsaCertKeyAlgoName)-1; + } + #endif /* WOLFSSH_OSSH_CERTS */ else { cannedKeyAlgoNames = cannedKeyAlgoRsaNames; cannedKeyAlgoNamesSz = cannedKeyAlgoRsaNamesSz; @@ -7917,37 +8412,39 @@ int SendKexInit(WOLFSSH* ssh) struct wolfSSH_sigKeyBlockFull { - byte pubKeyId; - byte sigId; - word32 sz; - const char *name; - word32 nameSz; - union { -#ifndef WOLFSSH_NO_SSH_RSA_SHA1 - struct { - RsaKey key; - byte e[257]; - word32 eSz; - byte ePad; - byte n[257]; - word32 nSz; - byte nPad; - } rsa; -#endif -#ifndef WOLFSSH_NO_ECDSA - struct { - ecc_key key; - word32 keyBlobSz; - const char *keyBlobName; - word32 keyBlobNameSz; - byte q[257]; - word32 qSz; - byte qPad; - const char *primeName; - word32 primeNameSz; - } ecc; -#endif - } sk; + byte pubKeyId; + byte sigId; + word32 sz; + const char *pubKeyName; + word32 pubKeyNameSz; + const char *sigName; + word32 sigNameSz; + union { + #ifndef WOLFSSH_NO_SSH_RSA_SHA1 + struct { + RsaKey key; + byte e[257]; + word32 eSz; + byte ePad; + byte n[257]; + word32 nSz; + byte nPad; + } rsa; + #endif + #ifndef WOLFSSH_NO_ECDSA + struct { + ecc_key key; + word32 keyBlobSz; + const char *keyBlobName; + word32 keyBlobNameSz; + byte q[257]; + word32 qSz; + byte qPad; + const char *primeName; + word32 primeNameSz; + } ecc; + #endif + } sk; }; #ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256_KYBER_LEVEL1_SHA256 @@ -8072,7 +8569,6 @@ static int SendKexGetSigningKey(WOLFSSH* ssh, word32 generatorSz = 0; #endif - heap = ssh->ctx->heap; switch (sigKeyBlock_ptr->pubKeyId) { @@ -8080,12 +8576,13 @@ static int SendKexGetSigningKey(WOLFSSH* ssh, case ID_X509V3_ECDSA_SHA2_NISTP256: case ID_X509V3_ECDSA_SHA2_NISTP384: case ID_X509V3_ECDSA_SHA2_NISTP521: - isCert = 1; + isCert = 1; + break; } - switch (sigKeyBlock_ptr->sigId) { + switch (sigKeyBlock_ptr->pubKeyId) { + #ifndef WOLFSSH_NO_SSH_RSA_SHA1 case ID_SSH_RSA: - #ifndef WOLFSSH_NO_SSH_RSA_SHA1 /* Decode the user-configured RSA private key. */ sigKeyBlock_ptr->sk.rsa.eSz = sizeof(sigKeyBlock_ptr->sk.rsa.e); sigKeyBlock_ptr->sk.rsa.nSz = sizeof(sigKeyBlock_ptr->sk.rsa.n); @@ -8095,7 +8592,7 @@ static int SendKexGetSigningKey(WOLFSSH* ssh, &sigKeyBlock_ptr->sk.rsa.key, (int)ssh->ctx->privateKeySz); - /* hash in usual public key if not RFC6187 style cert use */ + /* hash in usual public key if not an X.509 certificate */ if (!isCert) { /* Flatten the public key into mpint values for the hash. */ if (ret == 0) @@ -8118,7 +8615,7 @@ static int SendKexGetSigningKey(WOLFSSH* ssh, } if (ret == 0) { sigKeyBlock_ptr->sz = (LENGTH_SZ * 3) + - sigKeyBlock_ptr->nameSz + + sigKeyBlock_ptr->pubKeyNameSz + sigKeyBlock_ptr->sk.rsa.eSz + sigKeyBlock_ptr->sk.rsa.ePad + sigKeyBlock_ptr->sk.rsa.nSz + @@ -8130,15 +8627,15 @@ static int SendKexGetSigningKey(WOLFSSH* ssh, } /* Hash in the length of the key type string. */ if (ret == 0) { - c32toa(sigKeyBlock_ptr->nameSz, scratchLen); + c32toa(sigKeyBlock_ptr->pubKeyNameSz, scratchLen); ret = HashUpdate(&ssh->handshake->hash, enmhashId, scratchLen, LENGTH_SZ); } /* Hash in the key type string. */ if (ret == 0) ret = HashUpdate(&ssh->handshake->hash, enmhashId, - (byte*)sigKeyBlock_ptr->name, - sigKeyBlock_ptr->nameSz); + (byte*)sigKeyBlock_ptr->pubKeyName, + sigKeyBlock_ptr->pubKeyNameSz); /* Hash in the length of the RSA public key E value. */ if (ret == 0) { c32toa(sigKeyBlock_ptr->sk.rsa.eSz + @@ -8180,13 +8677,13 @@ static int SendKexGetSigningKey(WOLFSSH* ssh, sigKeyBlock_ptr->sk.rsa.n, sigKeyBlock_ptr->sk.rsa.nSz); } - #endif /* WOLFSSH_NO_SSH_RSA_SHA1 */ break; + #endif /* WOLFSSH_NO_SSH_RSA_SHA1 */ + #ifndef WOLFSSH_NO_ECDSA case ID_ECDSA_SHA2_NISTP256: case ID_ECDSA_SHA2_NISTP384: case ID_ECDSA_SHA2_NISTP521: - #ifndef WOLFSSH_NO_ECDSA sigKeyBlock_ptr->sk.ecc.primeName = PrimeNameForId(ssh->handshake->sigId); sigKeyBlock_ptr->sk.ecc.primeNameSz = @@ -8215,7 +8712,7 @@ static int SendKexGetSigningKey(WOLFSSH* ssh, /* Hash in the length of the public key block. */ if (ret == 0) { sigKeyBlock_ptr->sz = (LENGTH_SZ * 3) + - sigKeyBlock_ptr->nameSz + + sigKeyBlock_ptr->pubKeyNameSz + sigKeyBlock_ptr->sk.ecc.primeNameSz + sigKeyBlock_ptr->sk.ecc.qSz; c32toa(sigKeyBlock_ptr->sz, scratchLen); @@ -8224,15 +8721,15 @@ static int SendKexGetSigningKey(WOLFSSH* ssh, } /* Hash in the length of the key type string. */ if (ret == 0) { - c32toa(sigKeyBlock_ptr->nameSz, scratchLen); + c32toa(sigKeyBlock_ptr->pubKeyNameSz, scratchLen); ret = HashUpdate(&ssh->handshake->hash, enmhashId, scratchLen, LENGTH_SZ); } /* Hash in the key type string. */ if (ret == 0) ret = HashUpdate(&ssh->handshake->hash, enmhashId, - (byte*)sigKeyBlock_ptr->name, - sigKeyBlock_ptr->nameSz); + (byte*)sigKeyBlock_ptr->pubKeyName, + sigKeyBlock_ptr->pubKeyNameSz); /* Hash in the length of the name of the prime. */ if (ret == 0) { c32toa(sigKeyBlock_ptr->sk.ecc.primeNameSz, scratchLen); @@ -8255,14 +8752,37 @@ static int SendKexGetSigningKey(WOLFSSH* ssh, ret = HashUpdate(&ssh->handshake->hash, enmhashId, sigKeyBlock_ptr->sk.ecc.q, sigKeyBlock_ptr->sk.ecc.qSz); - } - #endif + } break; + #endif - default: - ret = WS_INVALID_ALGO_ID; - } + #ifdef WOLFSSH_OSSH_CERTS + case ID_OSSH_CERT_RSA: + /* Decode the user-configured RSA private key. */ + ret = wc_InitRsaKey(&sigKeyBlock_ptr->sk.rsa.key, heap); + if (ret == 0) { + ret = wc_RsaPrivateKeyDecode(ssh->ctx->privateKey, &scratch, + &sigKeyBlock_ptr->sk.rsa.key, + (int)ssh->ctx->privateKeySz); + } + if (ret == 0) { + sigKeyBlock_ptr->sz = ssh->ctx->osshCertRawSz; + c32toa(ssh->ctx->osshCertRawSz, scratchLen); + ret = HashUpdate(&ssh->handshake->hash, + enmhashId, + scratchLen, LENGTH_SZ); + } + if (ret == 0) { + ret = HashUpdate(&ssh->handshake->hash, enmhashId, + ssh->ctx->osshCertRaw, + ssh->ctx->osshCertRawSz); + } + break; + #endif /* WOLFSSH_OSSH_CERTS */ + default: + ret = WS_INVALID_ALGO_ID; + } /* if is RFC6187 then the hash of the public key is changed */ if (isCert) { @@ -8291,7 +8811,6 @@ static int SendKexGetSigningKey(WOLFSSH* ssh, #endif } - #ifndef WOLFSSH_NO_DH_GEX_SHA256 /* If using DH-GEX include the GEX specific values. */ if (ssh->handshake->kexId == ID_DH_GEX_SHA256) { @@ -8465,9 +8984,12 @@ int SendKexDhReply(WOLFSSH* ssh) if (ret == WS_SUCCESS) { sigKeyBlock_ptr->pubKeyId = ssh->handshake->pubKeyId; + sigKeyBlock_ptr->pubKeyName = IdToName(sigKeyBlock_ptr->pubKeyId); + sigKeyBlock_ptr->pubKeyNameSz = (word32)WSTRLEN(sigKeyBlock_ptr->pubKeyName); + sigKeyBlock_ptr->sigId = ssh->handshake->sigId; - sigKeyBlock_ptr->name = IdToName(ssh->handshake->sigId); - sigKeyBlock_ptr->nameSz = (word32)strlen(sigKeyBlock_ptr->name); + sigKeyBlock_ptr->sigName = IdToName(sigKeyBlock_ptr->sigId); + sigKeyBlock_ptr->sigNameSz = (word32)WSTRLEN(sigKeyBlock_ptr->sigName); switch (ssh->handshake->kexId) { #ifndef WOLFSSH_NO_DH_GROUP1_SHA1 @@ -8843,10 +9365,14 @@ int SendKexDhReply(WOLFSSH* ssh) if (sigKeyBlock_ptr->pubKeyId == ID_SSH_RSA #ifdef WOLFSSH_CERTS || sigKeyBlock_ptr->pubKeyId == ID_X509V3_SSH_RSA + #endif + #ifdef WOLFSSH_OSSH_CERTS + || sigKeyBlock_ptr->pubKeyId == ID_OSSH_CERT_RSA #endif ) { #ifndef WOLFSSH_NO_SSH_RSA_SHA1 word32 encSigSz; + int rc; #ifdef WOLFSSH_SMALL_STACK byte *encSig = (byte*)WMALLOC(MAX_ENCODED_SIG_SZ, heap, DYNTYPE_TEMP); @@ -8868,14 +9394,17 @@ int SendKexDhReply(WOLFSSH* ssh) } else { WLOG(WS_LOG_INFO, "Signing hash with %s.", - IdToName(ssh->handshake->pubKeyId)); - sigSz = wc_RsaSSL_Sign(encSig, encSigSz, sig_ptr, + sigKeyBlock_ptr->sigName); + rc = wc_RsaSSL_Sign(encSig, encSigSz, sig_ptr, KEX_SIG_SIZE, &sigKeyBlock_ptr->sk.rsa.key, ssh->rng); - if (sigSz <= 0) { + if (rc <= 0) { WLOG(WS_LOG_DEBUG, "SendKexDhReply: Bad RSA Sign"); ret = WS_RSA_E; } + else { + sigSz = rc; + } } #ifdef WOLFSSH_SMALL_STACK WFREE(encSig, heap, DYNTYPE_TEMP); @@ -8894,7 +9423,7 @@ int SendKexDhReply(WOLFSSH* ssh) ) { #ifndef WOLFSSH_NO_ECDSA WLOG(WS_LOG_INFO, "Signing hash with %s.", - IdToName(ssh->handshake->pubKeyId)); + sigKeyBlock_ptr->sigName); sigSz = KEX_SIG_SIZE; ret = wc_ecc_sign_hash(digest, wc_HashGetDigestSize(sigHashId), sig_ptr, &sigSz, @@ -8946,14 +9475,15 @@ int SendKexDhReply(WOLFSSH* ssh) } if (sigKeyBlock_ptr != NULL) { - if (sigKeyBlock_ptr->sigId == ID_SSH_RSA) { + if (sigKeyBlock_ptr->pubKeyId == ID_SSH_RSA || + sigKeyBlock_ptr->pubKeyId == ID_OSSH_CERT_RSA) { #ifndef WOLFSSH_NO_SSH_RSA_SHA1 wc_FreeRsaKey(&sigKeyBlock_ptr->sk.rsa.key); #endif } - else if (sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP256 || - sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP384 || - sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP521) { + else if (sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP256 || + sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP384 || + sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP521) { #ifndef WOLFSSH_NO_ECDSA wc_ecc_free(&sigKeyBlock_ptr->sk.ecc.key); #endif @@ -8966,7 +9496,7 @@ int SendKexDhReply(WOLFSSH* ssh) /* Get the buffer, copy the packet data, once f is laid into the buffer, * add it to the hash and then add K. */ if (ret == WS_SUCCESS) { - sigBlockSz = (LENGTH_SZ * 2) + sigKeyBlock_ptr->nameSz + sigSz; + sigBlockSz = (LENGTH_SZ * 2) + sigKeyBlock_ptr->sigNameSz + sigSz; payloadSz = MSG_ID_SZ + (LENGTH_SZ * 3) + sigKeyBlock_ptr->sz + fSz + fPad + sigBlockSz; ret = PreparePacket(ssh, payloadSz); @@ -8986,10 +9516,10 @@ int SendKexDhReply(WOLFSSH* ssh) /* Copy the rsaKeyBlock into the buffer. */ c32toa(sigKeyBlock_ptr->sz, output + idx); idx += LENGTH_SZ; - c32toa(sigKeyBlock_ptr->nameSz, output + idx); + c32toa(sigKeyBlock_ptr->pubKeyNameSz, output + idx); idx += LENGTH_SZ; - WMEMCPY(output + idx, sigKeyBlock_ptr->name, sigKeyBlock_ptr->nameSz); - idx += sigKeyBlock_ptr->nameSz; + WMEMCPY(output + idx, sigKeyBlock_ptr->pubKeyName, sigKeyBlock_ptr->pubKeyNameSz); + idx += sigKeyBlock_ptr->pubKeyNameSz; c32toa(sigKeyBlock_ptr->sk.rsa.eSz + sigKeyBlock_ptr->sk.rsa.ePad, output + idx); @@ -9012,13 +9542,12 @@ int SendKexDhReply(WOLFSSH* ssh) case ID_ECDSA_SHA2_NISTP521: { #ifndef WOLFSSH_NO_ECDSA - /* Copy the rsaKeyBlock into the buffer. */ c32toa(sigKeyBlock_ptr->sz, output + idx); idx += LENGTH_SZ; - c32toa(sigKeyBlock_ptr->nameSz, output + idx); + c32toa(sigKeyBlock_ptr->pubKeyNameSz, output + idx); idx += LENGTH_SZ; - WMEMCPY(output + idx, sigKeyBlock_ptr->name, sigKeyBlock_ptr->nameSz); - idx += sigKeyBlock_ptr->nameSz; + WMEMCPY(output + idx, sigKeyBlock_ptr->pubKeyName, sigKeyBlock_ptr->pubKeyNameSz); + idx += sigKeyBlock_ptr->pubKeyNameSz; c32toa(sigKeyBlock_ptr->sk.ecc.primeNameSz, output + idx); idx += LENGTH_SZ; @@ -9050,6 +9579,17 @@ int SendKexDhReply(WOLFSSH* ssh) } break; #endif + #ifdef WOLFSSH_OSSH_CERTS + case ID_OSSH_CERT_RSA: + { + c32toa(sigKeyBlock_ptr->sz, output + idx); + idx += LENGTH_SZ; + WMEMCPY(output + idx, ssh->ctx->osshCertRaw, + ssh->ctx->osshCertRawSz); + idx += ssh->ctx->osshCertRawSz; + } + break; + #endif /* WOLFSSH_OSSH_CERTS */ } } @@ -9062,12 +9602,14 @@ int SendKexDhReply(WOLFSSH* ssh) idx += fSz; /* Copy the signature of the exchange hash. */ + c32toa(sigBlockSz, output + idx); idx += LENGTH_SZ; - c32toa(sigKeyBlock_ptr->nameSz, output + idx); + c32toa(sigKeyBlock_ptr->sigNameSz, output + idx); idx += LENGTH_SZ; - WMEMCPY(output + idx, sigKeyBlock_ptr->name, sigKeyBlock_ptr->nameSz); - idx += sigKeyBlock_ptr->nameSz; + WMEMCPY(output + idx, sigKeyBlock_ptr->sigName, + sigKeyBlock_ptr->sigNameSz); + idx += sigKeyBlock_ptr->sigNameSz; c32toa(sigSz, output + idx); idx += LENGTH_SZ; WMEMCPY(output + idx, sig_ptr, sigSz); diff --git a/src/list.c b/src/list.c new file mode 100644 index 000000000..26008c15f --- /dev/null +++ b/src/list.c @@ -0,0 +1,206 @@ +/* list.c + * + * Copyright (C) 2014-2022 wolfSSL Inc. + * + * This file is part of wolfSSH. + * + * wolfSSH is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfSSH. If not, see . + */ + +#include +#include + +/* + * This is a singly-linked list implementation for use in wolfSSH. It's not + * designed to be a fully generic linked list; it doesn't support arbitrary + * types, and some changes are required to the implementation to support a new + * type. Additionally, not all list operations are supported at this time (e.g. + * node removal). Only those operations needed have been implemented. + * + * We needed a linked list for a few, distinct cases when developing support for + * OpenSSH-style certs, so we wrote this to improve code reuse. + */ + +static INLINE int TypeOk(byte type) +{ + return (type == LIST_OSSH_CA_KEY || type == LIST_OSSH_PRINCIPAL); +} + +/* + * Create a new list of the specified type. + */ +WOLFSSH_LIST* ListNew(byte type, void* heap) +{ + WOLFSSH_LIST* ret = NULL; + + if (TypeOk(type)) { + ret = (WOLFSSH_LIST*)WMALLOC(sizeof(WOLFSSH_LIST), heap, DYNTYPE_LIST); + if (ret != NULL) { + WMEMSET(ret, 0, sizeof(WOLFSSH_LIST)); + ret->type = type; + ret->heap = heap; + } + } + + return ret; +} + +static WOLFSSH_LIST_NODE* ListNodeNew(void* heap) +{ + WOLFSSH_LIST_NODE* ret = NULL; + + ret = (WOLFSSH_LIST_NODE*)WMALLOC(sizeof(WOLFSSH_LIST_NODE), heap, + DYNTYPE_LIST_NODE); + if (ret != NULL) { + WMEMSET(ret, 0, sizeof(WOLFSSH_LIST_NODE)); + } + + return ret; +} + +static void ListNodeFree(byte type, WOLFSSH_LIST_NODE* node) +{ + if (TypeOk(type) && node != NULL) { + switch (type) { + #ifdef WOLFSSH_OSSH_CERTS + case LIST_OSSH_CA_KEY: + OsshCaKeyFree(node->data.osshCaKey); + break; + case LIST_OSSH_PRINCIPAL: + OsshPrincipalFree(node->data.osshPrincipal); + break; + #endif /* WOLFSSH_OSSH_CERTS */ + default: + break; + } + + WFREE(node, heap, DYNTYPE_LIST_NODE); + } +} + +/* + * Free the list, all its individual nodes, and any data owned by those nodes. + */ +void ListFree(WOLFSSH_LIST* list) +{ + WOLFSSH_LIST_NODE* current; + WOLFSSH_LIST_NODE* next; + + if (list != NULL) { + current = list->head; + + while (current != NULL) { + next = current->next; + ListNodeFree(list->type, current); + current = next; + } + + WFREE(list, list->heap, DYNTYPE_LIST); + } +} + +/* + * Add an element to the front of the list. The data pointer should point to + * data whose type is supported by the list implementation. Returns WS_SUCCESS + * on success and negative values on failure. + */ +int ListAdd(WOLFSSH_LIST* list, void* data) +{ + int ret = WS_SUCCESS; + WOLFSSH_LIST_NODE* node; + + if (list == NULL || data == NULL) { + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { + node = ListNodeNew(list->heap); + if (node == NULL) { + ret = WS_MEMORY_E; + } + else { + node->data.raw = data; + if (list->head == NULL) { + list->head = node; + } + else { + node->next = list->head; + list->head = node; + } + } + } + + return ret; +} + +/* + * Search for an element matching in of size inSz in the list. The logic for + * "finding" such an element is specific to the type being searched for. If + * the element is in the list, returns 1. If not found, returns 0. If there's + * an error, returns negative values. + */ +int ListFind(WOLFSSH_LIST* list, const byte* in, word32 inSz) +{ + int ret = 0; + WOLFSSH_LIST_NODE* node; + + if (list == NULL || in == NULL || inSz == 0) { + ret = WS_BAD_ARGUMENT; + } + + node = list->head; + + if (ret == WS_SUCCESS) { + switch (list->type) { + WOLFSSH_OSSH_CA_KEY* key; + + case LIST_OSSH_CA_KEY: + { + while (node != NULL) { + key = node->data.osshCaKey; + if (key != NULL && + WC_SHA256_DIGEST_SIZE == inSz && + WMEMCMP(key->fingerprint, in, inSz) == 0) { + ret = 1; + break; + } + node = node->next; + } + break; + } + case LIST_OSSH_PRINCIPAL: + { + WOLFSSH_OSSH_PRINCIPAL* principal; + + while (node != NULL) { + principal = node->data.osshPrincipal; + if (principal != NULL && + principal->nameSz == inSz && + WMEMCMP(principal->name, in, inSz) == 0) { + ret = 1; + break; + } + node = node->next; + } + break; + } + default: + { + break; + } + } + } + + return ret; +} diff --git a/src/misc.c b/src/misc.c index 6ff0b2c8c..8db6c04f1 100644 --- a/src/misc.c +++ b/src/misc.c @@ -78,6 +78,113 @@ STATIC INLINE void ato32(const byte* c, word32* u32) } +/* + * These word64 functions come from wolfSSL. wolfSSL doesn't export them, so + * they're re-implemented here. + */ +#if defined(WORD64_AVAILABLE) && !defined(WOLFSSL_NO_WORD64_OPS) +STATIC INLINE word64 rotlFixed64(word64 x, word64 y) +{ + return (x << y) | (x >> (sizeof(y) * 8 - y)); +} + + +STATIC INLINE word64 ByteReverseWord64(word64 value) +{ +#if defined(WOLF_ALLOW_BUILTIN) && defined(__GNUC_PREREQ) && __GNUC_PREREQ(4, 3) + return (word64)__builtin_bswap64(value); +#elif defined(WOLFCRYPT_SLOW_WORD64) + return (word64)((word64)ByteReverseWord32((word32) value)) << 32 | + (word64)ByteReverseWord32((word32)(value >> 32)); +#else + value = ((value & W64LIT(0xFF00FF00FF00FF00)) >> 8) | + ((value & W64LIT(0x00FF00FF00FF00FF)) << 8); + value = ((value & W64LIT(0xFFFF0000FFFF0000)) >> 16) | + ((value & W64LIT(0x0000FFFF0000FFFF)) << 16); + return rotlFixed64(value, 32U); +#endif +} +#endif /* WORD64_AVAILABLE && !WOLFSSL_NO_WORD64_OPS */ + + +#ifdef WORD64_AVAILABLE +STATIC INLINE void ato64(const byte *in, w64wrapper *w64) +{ +#ifdef BIG_ENDIAN_ORDER + XMEMCPY(&w64->n, in, sizeof(w64->n)); +#else + word64 _in; + XMEMCPY(&_in, in, sizeof(_in)); + w64->n = ByteReverseWord64(_in); +#endif /* BIG_ENDIAN_ORDER */ +} + + +STATIC INLINE w64wrapper w64From32(word32 hi, word32 lo) +{ + w64wrapper ret; + ret.n = ((word64)hi << 32) | lo; + return ret; +} + + +STATIC INLINE byte w64GTE(w64wrapper a, w64wrapper b) +{ + return a.n >= b.n; +} + + +STATIC INLINE byte w64LT(w64wrapper a, w64wrapper b) +{ + return a.n < b.n; +} + +#else + +STATIC INLINE void ato64(const byte *in, w64wrapper *w64) +{ +#ifdef BIG_ENDIAN_ORDER + const word32 *_in = (const word32*)(in); + w64->n[0] = *_in; + w64->n[1] = *(_in + 1); +#else + ato32(in, &w64->n[0]); + ato32(in + 4, &w64->n[1]); +#endif /* BIG_ENDIAN_ORDER */ +} + + +STATIC INLINE w64wrapper w64From32(word32 hi, word32 lo) +{ + w64wrapper w64; + w64.n[0] = hi; + w64.n[1] = lo; + return w64; +} + + +STATIC INLINE byte w64GTE(w64wrapper a, w64wrapper b) +{ + if (a.n[0] > b.n[0]) + return 1; + if (a.n[0] == b.n[0]) + return a.n[1] >= b.n[1]; + return 0; +} + + +STATIC INLINE byte w64LT(w64wrapper a, w64wrapper b) +{ + if (a.n[0] < b.n[0]) + return 1; + if (a.n[0] == b.n[0]) + return a.n[1] < b.n[1]; + + return 0; +} +#endif /* WORD64_AVAILABLE */ + + /* convert 32 bit integer to opaque */ STATIC INLINE void c32toa(word32 u32, byte* c) { diff --git a/src/ossh_certs.c b/src/ossh_certs.c new file mode 100644 index 000000000..5b69acae5 --- /dev/null +++ b/src/ossh_certs.c @@ -0,0 +1,726 @@ +/* ossh_certs.c + * + * Copyright (C) 2014-2022 wolfSSL Inc. + * + * This file is part of wolfSSH. + * + * wolfSSH is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfSSH. If not, see . + */ + +#ifdef WOLFSSH_OSSH_CERTS + +#ifdef HAVE_CONFIG_H +#include +#endif /* HAVE_CONFIG_H */ + +#include +#ifdef NO_INLINE +#include +#else +#define WOLFSSH_MISC_INCLUDED +#include "src/misc.c" +#endif /* NO_INLINE */ +#include + +#include +#include + +static const byte osshCertTypes[] = { + ID_OSSH_CERT_RSA +}; +static const int NUM_OSSH_CERT_TYPES = (int)(sizeof(osshCertTypes) / + sizeof(*osshCertTypes)); + +enum { + OSSH_CERT_TYPE_USER = 1, + OSSH_CERT_TYPE_HOST = 2 +}; + +static int CheckAllowedCertType(byte type) { + int ret = WS_SUCCESS; + int i; + + for (i = 0; i < NUM_OSSH_CERT_TYPES; ++i) { + if (type == osshCertTypes[i]) { + break; + } + } + + if (i == NUM_OSSH_CERT_TYPES) { + WLOG(WS_LOG_ERROR, "Invalid OpenSSH cert type %u.", type); + ret = WS_INVALID_ALGO_ID; + } + + return ret; +} + +static int GetRsaParams(byte* in, word32 inSz, word32* idx, byte** e, + word32* eSz, byte** n, word32* nSz) +{ + int ret = WS_SUCCESS; + byte* tmp = NULL; + word32 tmpSz; + + if (in == NULL || inSz == 0 || idx == NULL || e == NULL || n == NULL) { + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { + /* Get exponent, e. */ + ret = GetStringRef(&tmpSz, &tmp, in, inSz, idx); + if (ret == WS_SUCCESS) { + *e = tmp; + *eSz = tmpSz; + /* Get modulus, n. */ + ret = GetStringRef(&tmpSz, &tmp, in, inSz, idx); + if (ret == WS_SUCCESS) { + *n = tmp; + *nSz = tmpSz; + } + else { + WLOG(WS_LOG_ERROR, "Failed to get RSA modulus, n."); + } + } + else { + WLOG(WS_LOG_ERROR, "Failed to get RSA exponent, e."); + } + } + + return ret; +} + +WOLFSSH_OSSH_PRINCIPAL* OsshPrincipalNew(void* heap) +{ + WOLFSSH_OSSH_PRINCIPAL* ret = NULL; + + ret = (WOLFSSH_OSSH_PRINCIPAL*)WMALLOC(sizeof(WOLFSSH_OSSH_PRINCIPAL), heap, + DYNTYPE_OSSH_PRINCIPAL); + if (ret != NULL) { + WMEMSET(ret, 0, sizeof(WOLFSSH_OSSH_PRINCIPAL)); + + ret->heap = heap; + } + + return ret; +} + +void OsshPrincipalFree(WOLFSSH_OSSH_PRINCIPAL* principal) +{ + if (principal != NULL) { + WFREE(principal, principal->heap, DYNTYPE_OSSH_PRINCIPAL); + } +} + +/* TODO: Test case with 0 valid principals? */ +static int GetValidPrincipals(byte* in, word32 inSz, word32* idx, + WOLFSSH_LIST** out, void* heap) +{ + int ret = WS_SUCCESS; + word32 listSz; + byte* name = NULL; + word32 nameSz; + WOLFSSH_LIST* list = NULL; + WOLFSSH_OSSH_PRINCIPAL* principal; + + if (in == NULL || idx == NULL || out == NULL || *out != NULL) { + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { + ret = GetUint32(&listSz, in, inSz, idx); + } + if (ret == WS_SUCCESS && listSz > 0) { + list = ListNew(LIST_OSSH_PRINCIPAL, heap); + if (list == NULL) { + ret = WS_MEMORY_E; + } + } + while (ret == WS_SUCCESS && listSz > 0) { + ret = GetStringRef(&nameSz, &name, in, inSz, idx); + if (ret == WS_SUCCESS) { + if (nameSz > MAX_OSSH_PRINCIPAL_MAX_NAME_SZ) { + ret = WS_BUFFER_E; + } + else { + principal = OsshPrincipalNew(heap); + if (principal == NULL) { + ret = WS_MEMORY_E; + } + else { + WMEMCPY(principal->name, name, nameSz); + principal->nameSz = nameSz; + ret = ListAdd(list, (void*)principal); + } + } + } + listSz -= (UINT32_SZ + nameSz); + } + + if (ret == WS_SUCCESS) { + *out = list; + } + else { + if (list != NULL) { + ListFree(list); + } + } + + return ret; +} + +static int GetCurrentTime(w64wrapper* out) +{ + int ret = WS_SUCCESS; + time_t currentTime; + + if (out == NULL) { + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { + currentTime = WTIME(NULL); + if (currentTime == (time_t)-1) { + ret = WS_FATAL_ERROR; + } + else { + if (sizeof(currentTime) == 8) { + *out = w64From32(currentTime >> 32, + currentTime & 0x00000000FFFFFFFF); + } + else if (sizeof(currentTime) == 4) { + *out = w64From32(0, currentTime); + } + else { + ret = WS_FATAL_ERROR; + } + } + } + + return ret; +} + +static int GetRsaKey(byte* in, word32 inSz, word32* idx, RsaKey** key, + void* heap) +{ + int ret = WS_SUCCESS; + byte* e; + word32 eSz; + byte* n; + word32 nSz; + RsaKey* tmpKey = NULL; + + if (in == NULL || inSz == 0 || idx == NULL || key == NULL || *key != NULL) { + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { + ret = GetRsaParams(in, inSz, idx, &e, &eSz, &n, &nSz); + if (ret == WS_SUCCESS) { + tmpKey = (RsaKey*)WMALLOC(sizeof(RsaKey), heap, DYNTYPE_PUBKEY); + if (tmpKey == NULL) { + ret = WS_MEMORY_E; + } + } + if (ret == WS_SUCCESS) { + ret = wc_InitRsaKey(tmpKey, heap); + if (ret != 0) { + WLOG(WS_LOG_ERROR, "Failed to initialize RSA key."); + ret = WS_RSA_E; + } + } + if (ret == WS_SUCCESS) { + ret = wc_RsaPublicKeyDecodeRaw(n, nSz, e, eSz, tmpKey); + if (ret != 0) { + WLOG(WS_LOG_ERROR, "Failed to create RSA key with parsed " + "params."); + ret = WS_RSA_E; + } + } + } + + if (ret == WS_SUCCESS) { + *key = tmpKey; + } + + return ret; +} + +static int GetCAKey(byte* in, word32 inSz, byte* keyId, byte* fingerprint, + void** key, void* heap) +{ + int ret = WS_SUCCESS; + word32 idx = 0; + byte* keyType = NULL; + word32 keyTypeSz; + byte tmpKeyId; + void* tmpKey = NULL; + + if (in == NULL || inSz == 0 || keyId == NULL || fingerprint == NULL || + key == NULL) { + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { + ret = GetStringRef(&keyTypeSz, &keyType, in, inSz, &idx); + } + if (ret == WS_SUCCESS) { + tmpKeyId = NameToId((char*)keyType, keyTypeSz); + + switch (tmpKeyId) { + case ID_SSH_RSA: + ret = GetRsaKey(in, inSz, &idx, (RsaKey**)&tmpKey, heap); + break; + default: + ret = WS_INVALID_ALGO_ID; + break; + } + } + + if (ret == WS_SUCCESS) { + ret = wc_Hash(WC_HASH_TYPE_SHA256, in, inSz, fingerprint, + WC_SHA256_DIGEST_SIZE); + } + if (ret == WS_SUCCESS) { + *keyId = tmpKeyId; + *key = tmpKey; + ret = idx; + } + else { + if (tmpKey != NULL) { + WFREE(tmpKey, heap, DYNTYPE_PUBKEY); + } + } + + return ret; +} + +static int GetCASignature(byte* in, word32 inSz, byte* sigId, byte** sig, + word32* sigSz) +{ + int ret = WS_SUCCESS; + word32 idx = 0; + byte* sigType = NULL; + word32 sigTypeSz; + byte tmpSigId; + byte* tmpSig = NULL; + word32 tmpSigSz; + + if (in == NULL || inSz == 0 || sigId == NULL || sig == NULL || + sigSz == NULL) { + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { + ret = GetStringRef(&sigTypeSz, &sigType, in, inSz, &idx); + } + if (ret == WS_SUCCESS) { + tmpSigId = NameToId((char*)sigType, sigTypeSz); + if (tmpSigId == ID_UNKNOWN) { + ret = WS_INVALID_ALGO_ID; + } + } + if (ret == WS_SUCCESS) { + ret = GetStringRef(&tmpSigSz, &tmpSig, in, inSz, &idx); + } + + if (ret == WS_SUCCESS) { + *sigId = tmpSigId; + *sig = tmpSig; + *sigSz = tmpSigSz; + ret = idx; + } + + return ret; +} + +static INLINE int CheckSigAndKeyTypes(byte sigId, byte keyId) +{ + int ret = WS_OSSH_CERT_CA_E; + + if (keyId == ID_SSH_RSA && (sigId == ID_SSH_RSA || + sigId == ID_RSA_SHA2_256 || sigId == ID_RSA_SHA2_512)) { + ret = WS_SUCCESS; + } + + return ret; +} + +static INLINE enum wc_HashType SigIdToWcHashType(byte sigId) +{ + enum wc_HashType ret = WC_HASH_TYPE_NONE; + + switch (sigId) { + case ID_SSH_RSA: + ret = WC_HASH_TYPE_SHA; + break; + case ID_RSA_SHA2_256: + ret = WC_HASH_TYPE_SHA256; + break; + case ID_RSA_SHA2_512: + ret = WC_HASH_TYPE_SHA512; + break; + default: + break; + } + + return ret; +} + +static INLINE enum wc_SignatureType SigIdToWcSigType(byte sigId) +{ + enum wc_SignatureType ret = WC_SIGNATURE_TYPE_NONE; + + switch (sigId) { + case ID_SSH_RSA: + case ID_RSA_SHA2_256: + case ID_RSA_SHA2_512: + ret = WC_SIGNATURE_TYPE_RSA_W_ENC; + break; + default: + break; + } + + return ret; +} + +static INLINE word32 GetKeyStructSz(byte keyId) +{ + word32 ret = 0; + + switch (keyId) { + case ID_SSH_RSA: + ret = sizeof(RsaKey); + break; + default: + break; + } + + return ret; +} + +static int CheckCASignature(const byte* tbs, word32 tbsSz, const byte* sig, + word32 sigSz, byte sigId, byte keyId, void* key) +{ + int ret = WS_SUCCESS; + enum wc_HashType wcHashType; + enum wc_SignatureType wcSigType; + word32 keySz; + + if (sig == NULL || sigSz == 0 || key == NULL) { + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { + ret = CheckSigAndKeyTypes(sigId, keyId); + } + if (ret == WS_SUCCESS) { + wcHashType = SigIdToWcHashType(sigId); + if (wcHashType == WC_HASH_TYPE_NONE) { + ret = WS_INVALID_ALGO_ID; + } + } + if (ret == WS_SUCCESS) { + wcSigType = SigIdToWcSigType(sigId); + if (wcSigType == WC_SIGNATURE_TYPE_NONE) { + ret = WS_INVALID_ALGO_ID; + } + } + if (ret == WS_SUCCESS) { + keySz = GetKeyStructSz(keyId); + if (keySz == 0) { + ret = WS_FATAL_ERROR; + } + } + if (ret == WS_SUCCESS) { + ret = wc_SignatureVerify(wcHashType, wcSigType, tbs, tbsSz, sig, sigSz, + key, keySz); + } + + return ret; +} + +WOLFSSH_OSSH_CERT* OsshCertNew(void* heap) +{ + WOLFSSH_OSSH_CERT* ret = NULL; + + ret = (WOLFSSH_OSSH_CERT*)WMALLOC(sizeof(WOLFSSH_OSSH_CERT), heap, + DYNTYPE_OSSH_CERT); + if (ret != NULL) { + WMEMSET(ret, 0, sizeof(WOLFSSH_OSSH_CERT)); + + ret->heap = heap; + } + + return ret; +} + +WOLFSSH_OSSH_CA_KEY* OsshCaKeyNew(void* heap) +{ + WOLFSSH_OSSH_CA_KEY* ret = NULL; + + ret = (WOLFSSH_OSSH_CA_KEY*)WMALLOC(sizeof(WOLFSSH_OSSH_CA_KEY), heap, + DYNTYPE_OSSH_CA_KEY); + if (ret != NULL) { + WMEMSET(ret, 0, sizeof(WOLFSSH_OSSH_CA_KEY)); + + ret->heap = heap; + } + + return ret; +} + +int OsshCaKeyInit(WOLFSSH_OSSH_CA_KEY* key, const byte* rawKey, word32 rawKeySz) +{ + int ret = WS_SUCCESS; + + if (key == NULL || rawKey == NULL || rawKeySz == 0) { + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { + ret = wc_Hash(WC_HASH_TYPE_SHA256, rawKey, rawKeySz, key->fingerprint, + WC_SHA256_DIGEST_SIZE); + } + + return ret; +} + +void OsshCertFree(WOLFSSH_OSSH_CERT* cert) +{ + if (cert != NULL) { + if (cert->principals != NULL) { + ListFree(cert->principals); + } + if (cert->pubKey != NULL) { + switch (cert->type) { + case ID_OSSH_CERT_RSA: + wc_FreeRsaKey((RsaKey*)cert->pubKey); + break; + default: + break; + } + WFREE(cert->pubKey, cert->heap, DYNTYPE_PUBKEY); + } + + WFREE(cert, cert->heap, DYNTYPE_OSSH_CERT); + } +} + +void OsshCaKeyFree(WOLFSSH_OSSH_CA_KEY* key) +{ + if (key != NULL) { + WFREE(key, key->heap, DYNTYPE_OSSH_CA_KEY); + } +} + +/* + * Parse the OpenSSH-style certificate held in buffer in of size inSz. Populate + * the passed in WOLFSSH_OSSH_CERT out with the result, allocating it if not + * already allocated by the caller. The validity of individual fields is checked + * where appropriate, according to the rules specified by OpenSSH (see + * https://man.openbsd.org/ssh-keygen#CERTIFICATES). Returns WS_SUCCESS on + * success and negative values on failure. + */ +int ParseOsshCert(byte* in, word32 inSz, WOLFSSH_OSSH_CERT** out, byte side, + void* heap) +{ + int ret = WS_SUCCESS; + byte* tmp = NULL; + word32 tmpSz; + word32 idx = 0; + byte keyId; + w64wrapper tmp64; + word32 tmp32; + w64wrapper time64; + byte caKeyId; + void* caKey = NULL; + byte* caSig = NULL; + word32 caSigSz; + byte caSigId; + byte allocCert = 0; + WOLFSSH_OSSH_CERT* certTmp = NULL; + WOLFSSH_LIST* principals = NULL; + + if (in == NULL || inSz == 0 || out == NULL) { + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { + /* Get cert type from key buffer. */ + ret = GetStringRef(&tmpSz, &tmp, in, inSz, &idx); + if (ret == WS_SUCCESS) { + keyId = NameToId((const char*)tmp, tmpSz); + ret = CheckAllowedCertType(keyId); + if (ret == WS_SUCCESS) { + if (*out == NULL) { + certTmp = OsshCertNew(heap); + if (certTmp == NULL) { + ret = WS_MEMORY_E; + } + else { + allocCert = 1; + certTmp->type = keyId; + } + } + else { + certTmp = *out; + } + } + } + } + if (ret == WS_SUCCESS) { + /* Get nonce. */ + ret = GetStringRef(&tmpSz, &tmp, in, inSz, &idx); + if (ret == WS_SUCCESS && tmpSz == 0) { + WLOG(WS_LOG_ERROR, "OpenSSH cert's nonce length must be > 0."); + ret = WS_OSSH_CERT_PARSE_E; + } + } + if (ret == WS_SUCCESS) { + switch (keyId) { + case ID_OSSH_CERT_RSA: + ret = GetRsaKey(in, inSz, &idx, (RsaKey**)&certTmp->pubKey, + heap); + break; + default: + ret = WS_INVALID_STATE_E; + break; + } + } + if (ret == WS_SUCCESS) { + /* + * Get serial number. Note that OpenSSH-style certs are allowed to have + * a serial number of 0, whereas X.509 certs aren't. + */ + ret = GetUint64(&tmp64, in, inSz, &idx); + } + if (ret == WS_SUCCESS) { + /* Get cert type (host or user). */ + ret = GetUint32(&tmp32, in, inSz, &idx); + if (ret == WS_SUCCESS) { + /* Check type is valid. */ + if (tmp32 != OSSH_CERT_TYPE_USER && tmp32 != OSSH_CERT_TYPE_HOST) { + ret = WS_OSSH_CERT_PARSE_E; + } + /* + * Check that the cert type is coherent with the side of the + * connection. + */ + else if ((tmp32 == OSSH_CERT_TYPE_USER && + side != WOLFSSH_ENDPOINT_CLIENT) || + (tmp32 == OSSH_CERT_TYPE_HOST && + side != WOLFSSH_ENDPOINT_SERVER)) { + ret = WS_OSSH_CERT_PARSE_E; + } + } + } + if (ret == WS_SUCCESS) { + /* Get key ID. */ + /* TODO: Any checking needed? */ + ret = GetStringRef(&tmpSz, &tmp, in, inSz, &idx); + } + if (ret == WS_SUCCESS) { + ret = GetValidPrincipals(in, inSz, &idx, &principals, heap); + if (ret == WS_SUCCESS) { + certTmp->principals = principals; + } + } + if (ret == WS_SUCCESS) { + /* Get current time, to check certificate expiry. */ + ret = GetCurrentTime(&time64); + } + if (ret == WS_SUCCESS) { + /* Get "valid after" time. */ + ret = GetUint64(&tmp64, in, inSz, &idx); + /* Current time must be after "valid after" time. */ + if (ret == WS_SUCCESS && w64LT(time64, tmp64)) { + ret = WS_OSSH_CERT_EXPIRED_E; + } + } + if (ret == WS_SUCCESS) { + /* Get "valid before" time. */ + ret = GetUint64(&tmp64, in, inSz, &idx); + /* Current time must be before "valid before" time. */ + if (ret == WS_SUCCESS && w64GTE(time64, tmp64)) { + ret = WS_OSSH_CERT_EXPIRED_E; + } + } + if (ret == WS_SUCCESS) { + /* Get critical options. Not supported, currently. Ensure length 0. */ + ret = GetStringRef(&tmpSz, &tmp, in, inSz, &idx); + if (ret == WS_SUCCESS && tmpSz != 0) { + ret = WS_OSSH_CERT_PARSE_E; + } + } + if (ret == WS_SUCCESS) { + /* Get extensions. Ignored, currently. */ + ret = GetStringRef(&tmpSz, &tmp, in, inSz, &idx); + } + if (ret == WS_SUCCESS) { + /* Reserved field. Should be a 0-length string. */ + ret = GetStringRef(&tmpSz, &tmp, in, inSz, &idx); + if (ret == WS_SUCCESS && tmpSz != 0) { + ret = WS_OSSH_CERT_PARSE_E; + } + } + if (ret == WS_SUCCESS) { + ret = GetUint32(&tmp32, in, inSz, &idx); + if (ret == WS_SUCCESS) { + ret = GetCAKey(in + idx, tmp32, &caKeyId, + certTmp->caKeyFingerprint, &caKey, heap); + if (ret > 0) { + idx += ret; + ret = WS_SUCCESS; + } + else { + ret = WS_OSSH_CERT_PARSE_E; + } + } + } + if (ret == WS_SUCCESS) { + ret = GetUint32(&tmp32, in, inSz, &idx); + if (ret == WS_SUCCESS) { + ret = GetCASignature(in + idx, tmp32, &caSigId, &caSig, &caSigSz); + if (ret > 0) { + if ((idx + ret) != inSz) { + ret = WS_OSSH_CERT_PARSE_E; + } + else { + ret = WS_SUCCESS; + } + } + } + } + if (ret == WS_SUCCESS) { + ret = CheckCASignature(in, idx - UINT32_SZ, caSig, caSigSz, caSigId, + caKeyId, caKey); + } + + if (caKey != NULL) { + WFREE(caKey, heap, DYNTYPE_PUBKEY); + } + + if (ret == WS_SUCCESS) { + *out = certTmp; + } + else { + if (allocCert && certTmp != NULL) { + OsshCertFree(certTmp); + } + } + + return ret; +} + +#endif /* WOLFSSH_OSSH_CERTS */ diff --git a/src/ssh.c b/src/ssh.c index 02b7bfdea..c39176f0b 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -1496,6 +1496,7 @@ int wolfSSH_ReadKey_buffer(const byte* in, word32 inSz, int format, const char* name; word32 typeSz; byte nameId; + int dynamicType = DYNTYPE_PRIVKEY; typeSz = (word32)WSTRLEN(type); @@ -1504,11 +1505,15 @@ int wolfSSH_ReadKey_buffer(const byte* in, word32 inSz, int format, *outType = (const byte*)name; *outTypeSz = typeSz; + if (nameId == ID_OSSH_CERT_RSA) { + dynamicType = DYNTYPE_PUBKEY; + } + if (*out == NULL) { /* set size based on sanity check in wolfSSL base64 decode * function */ *outSz = ((word32)WSTRLEN(key) * 3 + 3) / 4; - newKey = (byte*)WMALLOC(*outSz, heap, DYNTYPE_PRIVKEY); + newKey = (byte*)WMALLOC(*outSz, heap, dynamicType); if (newKey == NULL) { return WS_MEMORY_E; } @@ -1757,6 +1762,92 @@ int wolfSSH_CTX_AddRootCert_buffer(WOLFSSH_CTX* ctx, #endif /* WOLFSSH_CERTS */ +#ifdef WOLFSSH_OSSH_CERTS + +int wolfSSH_CTX_UseOsshCert_buffer(WOLFSSH_CTX* ctx, const byte* cert, + word32 certSz) +{ + int ret = WS_SUCCESS; + + WLOG(WS_LOG_DEBUG, "wolfSSH_CTX_UseOpenSSHCert_buffer()"); + + ret = wolfSSH_ProcessBuffer(ctx, cert, certSz, WOLFSSH_FORMAT_SSH, + BUFTYPE_OSSH_CERT); + + WLOG(WS_LOG_DEBUG, "wolfSSH_CTX_UseOpenSSHCert_buffer, ret = %d", ret); + + return ret; +} + +int wolfSSH_CTX_AddOsshCAKey(WOLFSSH_CTX* ctx, const byte* cert, + word32 certSz) +{ + int ret = WS_SUCCESS; + + WLOG(WS_LOG_DEBUG, "wolfSSH_CTX_AddOsshCAKey()"); + + ret = wolfSSH_ProcessBuffer(ctx, cert, certSz, WOLFSSH_FORMAT_SSH, + BUFTYPE_OSSH_CA_KEY); + + WLOG(WS_LOG_DEBUG, "wolfSSH_CTX_AddOsshCAKey, ret = %d", ret); + + return ret; +} + +int wolfSSH_CTX_AddOsshCAKeys_file(WOLFSSH_CTX* ctx, const char* file) +{ + int ret = WS_SUCCESS; + XFILE f; + char* current; + word32 currentSz; + enum { + MAX_LINE_SZ = 1024 + }; + char lineBuf[MAX_LINE_SZ]; + + WLOG(WS_LOG_DEBUG, "wolfSSH_CTX_AddOsshCAKeys_file()"); + + if (ctx == NULL || file == WBADFILE) { + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { + f = XFOPEN(file, "rb"); + if (f == XBADFILE) { + ret = WS_BAD_FILE_E; + } + + while (ret == WS_SUCCESS && + (current = XFGETS(lineBuf, MAX_LINE_SZ, f)) != NULL) { + currentSz = (word32)XSTRLEN(current); + + /* remove leading spaces */ + while (currentSz > 0 && current[0] == ' ') { + currentSz = currentSz - 1; + current = current + 1; + } + + if (currentSz <= 1) { + continue; /* empty line */ + } + + if (current[0] == '#') { + continue; /* commented out line */ + } + + ret = wolfSSH_CTX_AddOsshCAKey(ctx, (const byte*)current, + currentSz); + } + } + + WLOG(WS_LOG_DEBUG, "wolfSSH_CTX_AddOsshCAKeys_file, ret = %d", ret); + + return ret; +} + +#endif /* WOLFSSH_OSSH_CERTS */ + + int wolfSSH_CTX_SetWindowPacketSize(WOLFSSH_CTX* ctx, word32 windowSz, word32 maxPacketSz) { diff --git a/tests/api.c b/tests/api.c index a5a2e1769..4bb0ec019 100644 --- a/tests/api.c +++ b/tests/api.c @@ -537,6 +537,54 @@ static const char serverKeyRsaDer[] = "5f5bba6c42f121"; #endif +#ifdef WOLFSSH_OSSH_CERTS +#ifndef WOLFSSH_NO_RSA +static const char serverOsshCertRsa[] = + "ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNz" + "aC5jb20AAAAgMSbT5pFZeSb6CWe0PC8PTSUD1z5vhLU4vBYWzGh9wZ0AAAADAQABA" + "AABAQDaXa0lFHYVWfNA/Ty4YjCzbcD57OyLgx6eQpzKQWrTiuFSNOANE2J+1A+uXE" + "0E8Y36xa13qloFyu/4jav/iikJTATC9RnL7R+xtCnTw2ypI9+joOUI3q2Mcfk0iGz" + "tO/BvpQ+sWf9rM/Fw+4yks0UijZ13euUpX4QU2Znq6s4tUfPjWPpbAg/JtSq8sl7T" + "wjC7PLHD71jzUJQoi8RlSvcA2ZfZa02NlaGKYga0UBEig7TqKufQqCBHT/9GrsUT4" + "TiL+FSvOk0v+B/XhJDYkwUGwn2Q2+Oc0MRlWgOtAKxaos3aP4lYN1O/K0Z6rIlBK1" + "ou6HbnXuMphaNj6uaGYHwtAAAAAAAAAAAAAAACAAAACWxvY2FsaG9zdAAAAA0AAAA" + "JbG9jYWxob3N0AAAAAGL/J1gAAAAAdb37ogAAAAAAAAAAAAAAAAAAARcAAAAHc3No" + "LXJzYQAAAAMBAAEAAAEBANulfQhZqHoz6Ol5VBp6yJ+eIdovGMxeuQyBua6OTKu5P" + "0Ic/3pOXRVFuzMZov9itFLNcmvkHdDf0cr9B5ECUqDVygk7IEskKQHDFbqUXG1BN2" + "6STUeO3L9infYezPWvI1gOvZyWgKCz3tqtYIvHUPkcwjEH7pJRoWx4jwUdHOXHR3T" + "dXgLAUVVcm0SlfdNYy1I/d8LgFG/X+7sP+8GcpLbzb1jVGciAxTYYN+B/X0dIcjv4" + "TqiJdGPNLT8J/p7Dm86lpllbhFPX5RkFcZqWeJIW1CESe7n/0UP26ZQom8jqv8bZ3" + "rj5FzbELnUNMqzelzoR90UymNWwkg51UeAxUGMAAAEUAAAADHJzYS1zaGEyLTUxMg" + "AAAQA+IopQ4ESYtw+qRnO0ZIL4E3BUQveLBG7GG1GH+OmN6y+2dVHqdVq6IIkaXvv" + "eGXodkyFFtBbPHo/MVKmNz/8Ld7MRwff+H5JU1mkIy3tv4T4Yv7YZ+HwX0qWZ/5Nh" + "fZGQLU7E3ZXRirELPuuvGtpUhrQGHCkSrS8SDn637uX10o9Cxrcf4UhHhFmrocrXG" + "5TVHGDgASKHySkR9GCck/JdpO/mcIe5+bOMLnDtNq6nq7Fzrt6YK9dq7IrU6ybmY/" + "YKSkNbQsLwSh+pj9NRgeB1WqrwjXNkGQrL59gg1kHRZOh658UWVpQWlmS47Emt2cw" + "YLeTWF9o6u5Z5HuQpTDHO server-key-rsa.pub"; +#endif + +static void test_wolfSSH_CTX_UseOsshCert_buffer(void) +{ +#ifndef WOLFSSH_NO_SERVER + WOLFSSH_CTX* ctx; + int ret; + + AssertNotNull(ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL)); +#ifndef WOLFSSH_NO_RSA + ret = wolfSSH_CTX_UseOsshCert_buffer(ctx, (const byte *)serverOsshCertRsa, + sizeof(serverOsshCertRsa)); +#ifdef WOLFSSH_NO_SSH_RSA_SHA2_512 + /* The certificate uses SHA2-512 for the CA signature. */ + AssertIntEQ(ret, WS_INVALID_ALGO_ID); +#else + AssertIntEQ(ret, WS_SUCCESS); +#endif /* WOLFSSH_NO_SSH_RSA_SHA2_512 */ +#endif /* !WOLFSSH_NO_RSA */ + + wolfSSH_CTX_free(ctx); +#endif /* !WOLFSSH_NO_SERVER */ +} +#endif /* WOLFSSH_OSSH_CERTS */ static void test_wolfSSH_CTX_UsePrivateKey_buffer(void) { @@ -1163,6 +1211,9 @@ int main(void) test_wolfSSH_CTX_UsePrivateKey_buffer(); test_wolfSSH_CTX_UseCert_buffer(); test_wolfSSH_CertMan(); +#ifdef WOLFSSH_OSSH_CERTS + test_wolfSSH_CTX_UseOsshCert_buffer(); +#endif /* WOLFSSH_OSSH_CERTS */ /* SCP tests */ test_wolfSSH_SCP_CB(); diff --git a/tests/include.am b/tests/include.am index 39093a733..60db9aeab 100644 --- a/tests/include.am +++ b/tests/include.am @@ -57,6 +57,9 @@ endif if BUILD_CERTS tests_api_test_CPPFLAGS += -DWOLFSSH_CERTS endif +if BUILD_OPENSSH_CERTS +tests_api_test_CPPFLAGS += -DWOLFSSH_OSSH_CERTS +endif tests_api_test_LDADD = src/libwolfssh.la tests_api_test_DEPENDENCIES = src/libwolfssh.la @@ -93,3 +96,34 @@ tests_testsuite_test_DEPENDENCIES = src/libwolfssh.la DISTCLEANFILES+= tests/.libs/unit.test tests/.libs/api.test \ tests/.libs/testsuite.test EXTRA_DIST += tests/testsuite.h + +if BUILD_OPENSSH_CERTS + +noinst_PROGRAMS += tests/test_ossh_certs.test +check_PROGRAMS += tests/test_ossh_certs.test +tests_test_ossh_certs_test_SOURCES = tests/test_ossh_certs.c \ + src/internal.c \ + src/io.c \ + src/log.c \ + src/list.c \ + src/ossh_certs.c +tests_test_ossh_certs_test_LDADD = src/libwolfssh.la +tests_test_ossh_certs_test_DEPENDENCIES = src/libwolfssh.la +tests_test_ossh_certs_test_CPPFLAGS = $(AM_CPPFLAGS) -DWOLFSSH_OSSH_CERTS + +DISTCLEANFILES += tests/.libs/test_ossh_certs.test + +noinst_PROGRAMS += tests/test_list.test +check_PROGRAMS += tests/test_list.test + +tests_test_list_test_SOURCES = tests/test_list.c \ + src/io.c \ + src/internal.c \ + src/ossh_certs.c \ + src/list.c +tests_test_list_test_LDADD = src/libwolfssh.la +tests_test_list_test_DEPENDENCIES = src/libwolfssh.la + +DISTCLEANFILES += tests/.libs/test_list.test + +endif BUILD_OPENSSH_CERTS diff --git a/tests/test_list.c b/tests/test_list.c new file mode 100644 index 000000000..da720600f --- /dev/null +++ b/tests/test_list.c @@ -0,0 +1,227 @@ +#include +#include +#include +#include + +#ifdef WOLFSSH_OSSH_CERTS + +static int test_ListNew(void) +{ + int ret = WS_SUCCESS; + WOLFSSH_LIST* list; + typedef struct { + byte type; + const char* name; + } LIST_TYPE_VECTOR; + LIST_TYPE_VECTOR validTypes[] = { + {LIST_OSSH_CA_KEY, "LIST_OSSH_CA_KEY"}, + {LIST_OSSH_PRINCIPAL, "LIST_OSSH_PRINCIPAL"} + }; + word32 i; + + for (i = 0; i < sizeof(validTypes)/sizeof(*validTypes); ++i) { + fprintf(stderr, "Testing ListNew with valid type %s.\n", validTypes[i].name); + list = ListNew(validTypes[i].type, NULL); + if (list == NULL) { + fprintf(stderr, "ListNew with type %s failed.\n", + validTypes[i].name); + ret = WS_FATAL_ERROR; + break; + } + + ListFree(list); + } + + fprintf(stderr, "Testing ListNew with invalid type.\n"); + list = ListNew(-1, NULL); + if (list != NULL) { + fprintf(stderr, "ListNew with invalid type unexpectedly succeeded.\n"); + ListFree(list); + ret = WS_FATAL_ERROR; + } + + return ret; +} + +static int test_ListOps(void) +{ + int ret = WS_SUCCESS; + enum { + NUM_LISTS = 2, + NUM_NODES = 3, + BUF_SZ = 4 + }; + WOLFSSH_LIST* lists[NUM_LISTS] = {NULL, NULL}; + byte listTypes[NUM_LISTS] = { + LIST_OSSH_CA_KEY, + LIST_OSSH_PRINCIPAL + }; + word32 i; + word32 j; + const byte keyBufs[NUM_NODES][BUF_SZ] = { + {0xDE, 0xAD, 0xBE, 0xEF}, + {0x01, 0x02, 0x03, 0x04}, + {0x11, 0x22, 0x33, 0x44} + }; + WOLFSSH_OSSH_CA_KEY* keys[NUM_NODES]; + byte* keyFingerprints[NUM_NODES]; + const byte principalBufs[NUM_NODES][BUF_SZ] = { + {0x11, 0x10, 0x01, 0x00}, + {0xFF, 0xEE, 0xDD, 0xCC}, + {0x12, 0x34, 0x56, 0x78} + }; + WOLFSSH_OSSH_PRINCIPAL* principals[NUM_NODES]; + const byte* findElement; + word32 findSz; + const byte invalidElement[BUF_SZ] = {0}; + + for (i = 0; i < NUM_LISTS; ++i) { + lists[i] = ListNew(listTypes[i], NULL); + if (lists[i] == NULL) { + fprintf(stderr, "ListNew failed.\n"); + ret = WS_FATAL_ERROR; + } + + for (j = 0; ret == WS_SUCCESS && j < NUM_NODES; ++j) { + if (listTypes[i] == LIST_OSSH_CA_KEY) { + keys[j] = OsshCaKeyNew(NULL); + if (keys[j] != NULL) { + if (OsshCaKeyInit(keys[j], keyBufs[j], BUF_SZ) + == WS_SUCCESS) { + if (ListAdd(lists[i], keys[j]) != WS_SUCCESS) { + fprintf(stderr, "ListAdd failed.\n"); + ret = WS_FATAL_ERROR; + break; + } + else { + keyFingerprints[j] = keys[j]->fingerprint; + } + } + else { + fprintf(stderr, "OsshCaKeyInit failed.\n"); + ret = WS_FATAL_ERROR; + break; + } + } + else { + fprintf(stderr, "OsshCaKeyNew failed.\n"); + ret = WS_FATAL_ERROR; + break; + } + } + else { + principals[j] = OsshPrincipalNew(NULL); + if (principals[j] != NULL) { + WMEMCPY(principals[j]->name, principalBufs[j], BUF_SZ); + principals[j]->nameSz = BUF_SZ; + + if (ListAdd(lists[i], principals[j]) != WS_SUCCESS) { + fprintf(stderr, "ListAdd failed.\n"); + ret = WS_FATAL_ERROR; + break; + } + } + else { + fprintf(stderr, "OsshPrincipalNew failed.\n"); + ret = WS_FATAL_ERROR; + break; + } + } + } + } + + for (i = 0; ret == WS_SUCCESS && i < NUM_LISTS; ++i) { + for (j = 0; ret == WS_SUCCESS && j < NUM_NODES; ++j) { + if (listTypes[i] == LIST_OSSH_CA_KEY) { + findElement = keyFingerprints[j]; + findSz = WC_SHA256_DIGEST_SIZE; + } + else { + findElement = principalBufs[j]; + findSz = BUF_SZ; + } + + if (!ListFind(lists[i], findElement, findSz)) { + fprintf(stderr, "Failed to find element %u in list %u.\n", + j, i); + ret = WS_FATAL_ERROR; + break; + } + } + + if (ListFind(lists[i], invalidElement, sizeof(invalidElement))) { + fprintf(stderr, "Unexpectedly found element that wasn't added to " + "list.\n"); + ret = WS_FATAL_ERROR; + } + } + + for (i = 0; i < NUM_LISTS; ++i) { + ListFree(lists[i]); + } + + return ret; +} + +typedef int (*TEST_FUNC)(void); +typedef struct { + const char *name; + TEST_FUNC func; +} TEST_CASE; + +#define TEST_DECL(func) { #func, func } + +const TEST_CASE testCases[] = { + TEST_DECL(test_ListNew), + TEST_DECL(test_ListOps) +}; + +#define TEST_CASE_CNT (int)(sizeof(testCases) / sizeof(*testCases)) + +static void TestSetup(const TEST_CASE* tc) +{ + fprintf(stderr, "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 != WS_SUCCESS) { + fprintf(stderr, "%s FAILED.\n", tc->name); + } + else { + fprintf(stderr, "%s PASSED.\n", tc->name); + } + + TestCleanup(); + + return ret; +} + +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; +} + +#endif /* WOLFSSH_OSSH_CERTS */ diff --git a/tests/test_ossh_certs.c b/tests/test_ossh_certs.c new file mode 100644 index 000000000..c974596f2 --- /dev/null +++ b/tests/test_ossh_certs.c @@ -0,0 +1,191 @@ +#include +#include +#include +#include + +/* + * Reads an OpenSSH-style cert from file into buf. Returns the length of buf on + * success and negative values on failure. + */ +static int CertFileToBuffer(const char* file, char* buf, word32 bufSz) +{ + int ret; + WFILE* f; + char* line; + int lineSz; + + if (file == NULL || buf == NULL || bufSz == 0) { + ret = WS_BAD_ARGUMENT; + } + else { + if (WFOPEN(&f, file, "rb") != 0) { + ret = WS_BAD_FILE_E; + } + else { + line = WFGETS(buf, bufSz, f); + if (line == NULL) { + ret = WS_FATAL_ERROR; + } + else { + lineSz = (int)WSTRLEN(line); + + /* remove leading spaces */ + while (lineSz > 0 && line[0] == ' ') { + --lineSz;; + ++line; + } + + ret = lineSz; + } + } + } + + return ret; +} + +static int ParseOsshCertFromFile(const char* file, int side) +{ + int ret = WS_SUCCESS; + enum { + MAX_CERT_BUF_SZ = 2048 + }; + char fileBuf[MAX_CERT_BUF_SZ]; + int fileBufSz; + byte* certBuf = NULL; + word32 certBufSz = 0; + const byte* certType; + word32 certTypeSz; + WOLFSSH_OSSH_CERT* cert = NULL; + + fileBufSz = CertFileToBuffer(file, fileBuf, sizeof(fileBuf)); + if (fileBufSz < 0) { + fprintf(stderr, "Failed to read %s into buffer.\n", file); + ret = WS_FATAL_ERROR; + } + if (ret == WS_SUCCESS) { + ret = wolfSSH_ReadKey_buffer((byte*)fileBuf, fileBufSz, + WOLFSSH_FORMAT_SSH, &certBuf, &certBufSz, &certType, + &certTypeSz, NULL); + if (ret != WS_SUCCESS) { + fprintf(stderr, "wolfSSH_ReadKey_buffer for file %s failed.\n", + file); + } + } + if (ret == WS_SUCCESS) { + ret = ParseOsshCert(certBuf, certBufSz, &cert, side, NULL); + if (ret != WS_SUCCESS) { + fprintf(stderr, "ParseOsshCert for %s failed.\n", file); + } + } + if (cert != NULL) { + OsshCertFree(cert); + } + if (certBuf != NULL) { + WFREE(certBuf, NULL, DYNTYPE_PUBKEY); + } + + return ret; +} + +static int test_ParseOsshCert(void) +{ + int ret; + static const char* validHostRsaCert = + "keys/ossh/ossh-host-rsa-key-cert.pub"; + static const char* validUserRsaCert = + "keys/ossh/ossh-user-rsa-key-cert.pub"; + + fprintf(stderr, "Testing ParseOsshCert w/ valid host RSA cert.\n"); + ret = ParseOsshCertFromFile(validHostRsaCert, WOLFSSH_ENDPOINT_SERVER); + + if (ret == WS_SUCCESS) { + fprintf(stderr, "Testing ParseOsshCert w/ valid user RSA cert.\n"); + ret = ParseOsshCertFromFile(validUserRsaCert, WOLFSSH_ENDPOINT_CLIENT); + } + + if (ret == WS_SUCCESS) { + fprintf(stderr, "Testing ParseOsshCert w/ valid host RSA cert but " + "wrong side.\n"); + ret = ParseOsshCertFromFile(validHostRsaCert, WOLFSSH_ENDPOINT_CLIENT); + if (ret == WS_SUCCESS) { + ret = WS_FATAL_ERROR; + } + else { + ret = WS_SUCCESS; + } + } + + if (ret == WS_SUCCESS) { + fprintf(stderr, "Testing ParseOsshCert w/ valid user RSA cert but " + "wrong side.\n"); + ret = ParseOsshCertFromFile(validUserRsaCert, WOLFSSH_ENDPOINT_SERVER); + if (ret == WS_SUCCESS) { + ret = WS_FATAL_ERROR; + } + else { + ret = WS_SUCCESS; + } + } + + return ret; +} + +typedef int (*TEST_FUNC)(void); +typedef struct { + const char *name; + TEST_FUNC func; +} TEST_CASE; + +#define TEST_DECL(func) { #func, func } + +const TEST_CASE testCases[] = { + TEST_DECL(test_ParseOsshCert) +}; + +#define TEST_CASE_CNT (int)(sizeof(testCases) / sizeof(*testCases)) + +static void TestSetup(const TEST_CASE* tc) +{ + fprintf(stderr, "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 != WS_SUCCESS) { + fprintf(stderr, "%s FAILED.\n", tc->name); + } + else { + fprintf(stderr, "%s PASSED.\n", tc->name); + } + + TestCleanup(); + + return ret; +} + +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; +} diff --git a/wolfssh/error.h b/wolfssh/error.h index 41f6676e9..e420c2584 100644 --- a/wolfssh/error.h +++ b/wolfssh/error.h @@ -125,9 +125,12 @@ enum WS_ErrorCodes { WS_CERT_SIG_CONFIRM_E = -1084, /* Root cert sig verify fail */ WS_CERT_OTHER_E = -1085, /* Other certificate issue */ WS_CERT_PROFILE_E = -1086, /* Cert doesn't meet profile reqs */ - WS_CERT_KEY_SIZE_E = -1087, /* Key size error */ + WS_CERT_KEY_SIZE_E = -1087, /* Key size error */ + WS_OSSH_CERT_PARSE_E = -1088, /* Error parsing OpenSSH-style cert */ + WS_OSSH_CERT_CA_E = -1089, /* Error with OpenSSH-style cert's CA */ + WS_OSSH_CERT_EXPIRED_E = -1090, /* OpenSSH-style cert dates not valid. */ - WS_LAST_E = -1087 /* Update this to indicate last error */ + WS_LAST_E = -1090 /* Update this to indicate last error */ }; diff --git a/wolfssh/include.am b/wolfssh/include.am index f4013bf20..54cf7faf8 100644 --- a/wolfssh/include.am +++ b/wolfssh/include.am @@ -19,5 +19,6 @@ nobase_include_HEADERS+= \ wolfssh/certs_test.h \ wolfssh/wolfsftp.h -noinst_HEADERS+= wolfssh/internal.h - +noinst_HEADERS+= wolfssh/internal.h \ + wolfssh/ossh_certs.h \ + wolfssh/list.h diff --git a/wolfssh/internal.h b/wolfssh/internal.h index aa5406bd3..3dab9055d 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -293,6 +294,7 @@ enum { ID_X509V3_ECDSA_SHA2_NISTP256, ID_X509V3_ECDSA_SHA2_NISTP384, ID_X509V3_ECDSA_SHA2_NISTP521, + ID_OSSH_CERT_RSA, /* Service IDs */ ID_SERVICE_USERAUTH, @@ -312,6 +314,9 @@ enum { ID_GLOBREQ_TCPIP_FWD, ID_GLOBREQ_TCPIP_FWD_CANCEL, + ID_RSA_SHA2_256, + ID_RSA_SHA2_512, + ID_UNKNOWN }; @@ -333,6 +338,7 @@ enum { #define MSG_ID_SZ 1 #define SHA1_96_SZ 12 #define UINT32_SZ 4 +#define UINT64_SZ 8 #define SSH_PROTO_SZ 7 /* "SSH-2.0" */ #define AEAD_IMP_IV_SZ 4 #define AEAD_EXP_IV_SZ 8 @@ -448,9 +454,21 @@ struct WOLFSSH_CTX { word32 maxPacketSz; byte side; /* client or server */ byte showBanner; +#ifdef WOLFSSH_OSSH_CERTS + WOLFSSH_OSSH_CERT* osshCert; + byte* osshCertRaw; + word32 osshCertRawSz; + WOLFSSH_LIST* osshCAKeys; +#endif /* WOLFSSH_OSSH_CERTS */ #ifdef WOLFSSH_AGENT byte agentEnabled; #endif /* WOLFSSH_AGENT */ +#ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 + byte useSaber:1; /* Depends on the private key */ +#endif +#ifdef WOLFSSH_OSSH_CERTS + byte useOsshCert:1; +#endif /* WOLFSSH_OSSH_CERTS */ }; @@ -791,6 +809,7 @@ WOLFSSH_LOCAL int wolfSSH_FwdWorker(WOLFSSH*); /* Parsing functions */ WOLFSSH_LOCAL int GetBoolean(byte*, byte*, word32, word32*); WOLFSSH_LOCAL int GetUint32(word32*, const byte*, word32, word32*); +WOLFSSH_LOCAL int GetUint64(w64wrapper*, const byte*, word32, word32*); WOLFSSH_LOCAL int GetSize(word32*, const byte*, word32, word32*); WOLFSSH_LOCAL int GetMpint(word32*, byte**, byte*, word32, word32*); WOLFSSH_LOCAL int GetString(char*, word32*, byte*, word32, word32*); @@ -1012,7 +1031,12 @@ enum WS_DynamicTypes { DYNTYPE_FILE, DYNTYPE_TEMP, DYNTYPE_PATH, - DYNTYPE_SSHD + DYNTYPE_SSHD, + DYNTYPE_OSSH_CERT, + DYNTYPE_OSSH_PRINCIPAL, + DYNTYPE_OSSH_CA_KEY, + DYNTYPE_LIST, + DYNTYPE_LIST_NODE, }; @@ -1020,7 +1044,11 @@ enum WS_BufferTypes { BUFTYPE_CA, BUFTYPE_CERT, BUFTYPE_PRIVKEY, - BUFTYPE_PUBKEY + BUFTYPE_PUBKEY, +#ifdef WOLFSSH_OSSH_CERTS + BUFTYPE_OSSH_CERT, + BUFTYPE_OSSH_CA_KEY +#endif /* WOLFSSH_OSSH_CERTS */ }; diff --git a/wolfssh/list.h b/wolfssh/list.h new file mode 100644 index 000000000..d0f7e57b7 --- /dev/null +++ b/wolfssh/list.h @@ -0,0 +1,73 @@ +/* list.h + * + * Copyright (C) 2014-2022 wolfSSL Inc. + * + * This file is part of wolfSSH. + * + * wolfSSH is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfSSH. If not, see . + */ + +#ifndef _WOLFSSH_LIST_H_ +#define _WOLFSSH_LIST_H_ + +#ifdef WOLFSSL_USER_SETTINGS +#include +#else +#include +#endif +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct WOLFSSH_OSSH_CA_KEY; +struct WOLFSSH_OSSH_PRINCIPAL; + +struct WOLFSSH_LIST_NODE { + byte type; + union { + #ifdef WOLFSSH_OSSH_CERTS + struct WOLFSSH_OSSH_CA_KEY* osshCaKey; + struct WOLFSSH_OSSH_PRINCIPAL* osshPrincipal; + #endif /* WOLFSSH_OSSH_CERTS */ + void* raw; + } data; + struct WOLFSSH_LIST_NODE* next; +}; +typedef struct WOLFSSH_LIST_NODE WOLFSSH_LIST_NODE; + +typedef struct { + byte type; + WOLFSSH_LIST_NODE* head; + void* heap; +} WOLFSSH_LIST; + +enum { + LIST_OSSH_CA_KEY = 1, + LIST_OSSH_PRINCIPAL = 2 +}; + +WOLFSSH_LOCAL WOLFSSH_LIST* ListNew(byte type, void* heap); +WOLFSSH_LOCAL void ListFree(WOLFSSH_LIST* list); +WOLFSSH_LOCAL int ListAdd(WOLFSSH_LIST* list, void* data); +WOLFSSH_LOCAL int ListFind(WOLFSSH_LIST* list, const byte* in, word32 inSz); + +#ifdef __cplusplus +} +#endif + +#endif /* _WOLFSSH_LIST_H_ */ diff --git a/wolfssh/misc.h b/wolfssh/misc.h index 32bc0af31..85b81a56f 100644 --- a/wolfssh/misc.h +++ b/wolfssh/misc.h @@ -42,9 +42,21 @@ WOLFSSH_LOCAL word32 min(word32, word32); WOLFSSH_LOCAL void ato32(const byte*, word32*); WOLFSSH_LOCAL void c32toa(word32, byte*); + +WOLFSSH_LOCAL w64wrapper w64From32(word32, word32); +WOLFSSH_LOCAL byte w64GTE(w64wrapper, w64wrapper); +WOLFSSH_LOCAL byte w64LT(w64wrapper, w64wrapper); + WOLFSSH_LOCAL void ForceZero(const void*, word32); WOLFSSH_LOCAL int ConstantCompare(const byte*, const byte*, word32); +WOLFSSH_LOCAL word64 rotlFixed64(word64 x, word64 y); +WOLFSSH_LOCAL word64 ByteReverseWord64(word64 value); +#ifdef WORD64_AVAILABLE +WOLFSSH_LOCAL void ato64(const byte *in, w64wrapper *w64); +#else +void ato64(const byte *in, w64wrapper *w64) +#endif /* WORD64_AVAILABLE */ #endif /* NO_INLINE */ diff --git a/wolfssh/ossh_certs.h b/wolfssh/ossh_certs.h new file mode 100644 index 000000000..73cb36dba --- /dev/null +++ b/wolfssh/ossh_certs.h @@ -0,0 +1,85 @@ +/* ossh_certs.h + * + * Copyright (C) 2014-2022 wolfSSL Inc. + * + * This file is part of wolfSSH. + * + * wolfSSH is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfSSH is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfSSH. If not, see . + */ + +#ifndef _WOLFSSH_OSSH_CERTS_H_ +#define _WOLFSSH_OSSH_CERTS_H_ + +#ifdef WOLFSSH_OSSH_CERTS + +#ifdef WOLFSSL_USER_SETTINGS +#include +#else +#include +#endif +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + MAX_OSSH_PRINCIPAL_MAX_NAME_SZ = 32 +}; + +struct WOLFSSH_OSSH_PRINCIPAL { + byte name[MAX_OSSH_PRINCIPAL_MAX_NAME_SZ]; + word32 nameSz; + void* heap; +}; +typedef struct WOLFSSH_OSSH_PRINCIPAL WOLFSSH_OSSH_PRINCIPAL; + +typedef struct { + byte type; + void* pubKey; + byte caKeyFingerprint[WC_SHA256_DIGEST_SIZE]; + WOLFSSH_LIST* principals; + void* heap; +} WOLFSSH_OSSH_CERT; + +struct WOLFSSH_OSSH_CA_KEY { + byte fingerprint[WC_SHA256_DIGEST_SIZE]; + void* heap; +}; +typedef struct WOLFSSH_OSSH_CA_KEY WOLFSSH_OSSH_CA_KEY; + +WOLFSSH_LOCAL WOLFSSH_OSSH_CERT* OsshCertNew(void* heap); +WOLFSSH_LOCAL void OsshCertFree(WOLFSSH_OSSH_CERT* cert); +WOLFSSH_LOCAL int ParseOsshCert(byte* in, word32 inSz, WOLFSSH_OSSH_CERT** out, + byte side, void* heap); + +WOLFSSH_LOCAL WOLFSSH_OSSH_PRINCIPAL* OsshPrincipalNew(void* heap); +WOLFSSH_LOCAL void OsshPrincipalFree(WOLFSSH_OSSH_PRINCIPAL* principal); + +WOLFSSH_LOCAL WOLFSSH_OSSH_CA_KEY* OsshCaKeyNew(void* heap); +WOLFSSH_LOCAL int OsshCaKeyInit(WOLFSSH_OSSH_CA_KEY* key, const byte* rawKey, + word32 rawKeySz); +WOLFSSH_LOCAL void OsshCaKeyFree(WOLFSSH_OSSH_CA_KEY* key); + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFSSH_OSSH_CERTS */ + +#endif /* _WOLFSSH_OSSH_CERTS_H_ */ diff --git a/wolfssh/ssh.h b/wolfssh/ssh.h index a48b48a2b..c211792c9 100644 --- a/wolfssh/ssh.h +++ b/wolfssh/ssh.h @@ -191,7 +191,10 @@ typedef struct WS_UserAuthData_PublicKey { byte hasSignature; const byte* signature; word32 signatureSz; + const byte* caKey; + word32 caKeySz; byte isCert:1; + byte isOsshCert:1; } WS_UserAuthData_PublicKey; typedef struct WS_UserAuthData { @@ -239,11 +242,20 @@ WOLFSSH_API int wolfSSH_CTX_SetBanner(WOLFSSH_CTX*, const char*); WOLFSSH_API int wolfSSH_CTX_UsePrivateKey_buffer(WOLFSSH_CTX*, const byte*, word32, int); #ifdef WOLFSSH_CERTS - WOLFSSH_API int wolfSSH_CTX_UseCert_buffer(WOLFSSH_CTX* ctx, - const byte* cert, word32 certSz, int format); - WOLFSSH_API int wolfSSH_CTX_AddRootCert_buffer(WOLFSSH_CTX* ctx, - const byte* cert, word32 certSz, int format); +WOLFSSH_API int wolfSSH_CTX_UseCert_buffer(WOLFSSH_CTX* ctx, const byte* cert, + word32 certSz, int format); +WOLFSSH_API int wolfSSH_CTX_AddRootCert_buffer(WOLFSSH_CTX* ctx, + const byte* cert, word32 certSz, int format); #endif /* WOLFSSH_CERTS */ +#ifdef WOLFSSH_OSSH_CERTS +WOLFSSH_API int wolfSSH_CTX_UseOsshCert_buffer(WOLFSSH_CTX* ctx, + const byte* cert, word32 certSz); +WOLFSSH_API int wolfSSH_CTX_AddOsshCAKey(WOLFSSH_CTX* ctx, + const byte* cert, word32 certSz); +WOLFSSH_API int wolfSSH_CTX_AddOsshCAKeys_file(WOLFSSH_CTX* ctx, + const char* file); +#endif /* WOLFSSH_OSSH_CERTS */ + WOLFSSH_API int wolfSSH_CTX_SetWindowPacketSize(WOLFSSH_CTX*, word32, word32); WOLFSSH_API int wolfSSH_accept(WOLFSSH*);