diff --git a/.gitignore b/.gitignore index 865755744..c73676e9b 100644 --- a/.gitignore +++ b/.gitignore @@ -74,8 +74,10 @@ client.plist *.sdf *.v11.suo *.vcxproj.filters +*.vcxproj.user *.opensdf *.pdb +.vs Debug Release DLL Debug diff --git a/apps/wolfsshd/auth.c b/apps/wolfsshd/auth.c index 89adb5ce3..4da1fb8d8 100644 --- a/apps/wolfsshd/auth.c +++ b/apps/wolfsshd/auth.c @@ -22,6 +22,12 @@ #include #endif +#ifdef WOLFSSL_USER_SETTINGS + #include +#else + #include +#endif + #ifdef WOLFSSH_SSHD #ifdef __linux__ @@ -30,7 +36,10 @@ #define _GNU_SOURCE #endif #endif + +#ifndef _WIN32 #include +#endif #include #include @@ -69,6 +78,9 @@ struct WOLFSSHD_AUTH { CallbackCheckPassword checkPasswordCb; CallbackCheckPublicKey checkPublicKeyCb; const WOLFSSHD_CONFIG* conf; +#if defined(_WIN32) + HANDLE token; /* a users token */ +#endif int gid; int uid; int sGid; /* saved gid */ @@ -143,12 +155,13 @@ static int CheckAuthKeysLine(char* line, word32 lineSz, const byte* key, word32 keySz) { int ret = WSSHD_AUTH_SUCCESS; - char* type; - char* keyCandBase64; /* cand == candidate */ + char* type = NULL; + char* keyCandBase64 = NULL; /* cand == candidate */ word32 keyCandBase64Sz; byte* keyCand = NULL; - word32 keyCandSz; - char* last; + word32 keyCandSz = 0; + char* last = NULL; + enum { #ifdef WOLFSSH_CERTS NUM_ALLOWED_TYPES = 9 @@ -329,7 +342,7 @@ static int CheckPasswordHashUnix(const char* input, char* stored) } #endif /* WOLFSSH_HAVE_LIBCRYPT || WOLFSSH_HAVE_LIBLOGIN */ -static int CheckPasswordUnix(const char* usr, const byte* pw, word32 pwSz) +static int CheckPasswordUnix(const char* usr, const byte* pw, word32 pwSz, WOLFSSHD_AUTH* authCtx) { int ret = WS_SUCCESS; char* pwStr = NULL; @@ -413,35 +426,16 @@ static int CheckPasswordUnix(const char* usr, const byte* pw, word32 pwSz) WFREE(storedHashCpy, NULL, DYNTYPE_STRING); } + WOLFSSH_UNUSED(authCtx); return ret; } #endif /* WOLFSSH_USE_PAM */ #endif /* !_WIN32 */ -#ifndef _WIN32 -static int CheckUserUnix(const char* name) { - int ret = WSSHD_AUTH_FAILURE; - struct passwd* pwInfo; - - wolfSSH_Log(WS_LOG_INFO, "[SSHD] Unix check user"); - errno = 0; - pwInfo = getpwnam(name); - if (pwInfo == NULL) { - if (errno != 0) { - wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error calling getpwnam for user " - "%s.", name); - ret = WS_FATAL_ERROR; - } - } - else { - ret = WSSHD_AUTH_SUCCESS; - } - return ret; -} static const char authKeysDefault[] = ".ssh/authorized_keys"; -static char authKeysPattern[MAX_PATH_SZ] = {0}; +static char authKeysPattern[MAX_PATH_SZ] = { 0 }; void SetAuthKeysPattern(const char* pattern) { @@ -451,6 +445,7 @@ void SetAuthKeysPattern(const char* pattern) } } + static int ResolveAuthKeysPath(const char* homeDir, char* resolved) { int ret = WS_SUCCESS; @@ -481,7 +476,7 @@ static int ResolveAuthKeysPath(const char* homeDir, char* resolved) if (homeDirSz + 1 + WSTRLEN(suffix) >= MAX_PATH_SZ) { wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Path for key file larger than max allowed"); - ret = WS_FATAL_ERROR; + ret = WS_FATAL_ERROR; } if (ret == WS_SUCCESS) { @@ -496,23 +491,111 @@ static int ResolveAuthKeysPath(const char* homeDir, char* resolved) return ret; } -static int CheckPublicKeyUnix(const char* name, - const WS_UserAuthData_PublicKey* pubKeyCtx, - const char* usrCaKeysFile) +static int SearchForPubKey(const char* path, const WS_UserAuthData_PublicKey* pubKeyCtx) { int ret = WSSHD_AUTH_SUCCESS; - int rc; - struct passwd* pwInfo; - char* authKeysFile = NULL; - XFILE f = XBADFILE; + char authKeysPath[MAX_PATH_SZ]; + WFILE *f = XBADFILE; char* lineBuf = NULL; char* current; word32 currentSz; int foundKey = 0; - char authKeysPath[MAX_PATH_SZ]; + int rc = 0; + + WMEMSET(authKeysPath, 0, sizeof(authKeysPath)); + rc = ResolveAuthKeysPath(path, authKeysPath); + if (rc != WS_SUCCESS) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failed to resolve authorized keys" + " file path."); + ret = rc; + } + + if (ret == WSSHD_AUTH_SUCCESS) { + if (WFOPEN(NULL, &f, authKeysPath, "rb") != 0) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Unable to open %s", + authKeysPath); + 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 = WFGETS(lineBuf, MAX_LINE_SZ, f)) != NULL) { + currentSz = (word32)WSTRLEN(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 = CheckAuthKeysLine(current, currentSz, pubKeyCtx->publicKey, + pubKeyCtx->publicKeySz); + if (rc == WSSHD_AUTH_SUCCESS) { + foundKey = 1; + break; + } + else if (rc < 0) { + ret = rc; + break; + } + } + + if (f != WBADFILE) { + WFCLOSE(NULL, f); + } + + if (ret == WSSHD_AUTH_SUCCESS && !foundKey) { + ret = WSSHD_AUTH_FAILURE; + } + + return ret; +} + +#ifndef _WIN32 +static int CheckUserUnix(const char* name) { + int ret = WSSHD_AUTH_FAILURE; + struct passwd* pwInfo; + + wolfSSH_Log(WS_LOG_INFO, "[SSHD] Unix check user"); + errno = 0; + pwInfo = getpwnam(name); + if (pwInfo == NULL) { + if (errno != 0) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error calling getpwnam for user " + "%s.", name); + ret = WS_FATAL_ERROR; + } + } + else { + ret = WSSHD_AUTH_SUCCESS; + } + + return ret; +} + +static int CheckPublicKeyUnix(const char* name, + const WS_UserAuthData_PublicKey* pubKeyCtx, + const char* usrCaKeysFile, WOLFSSHD_AUTH* authCtx) +{ + int ret = WSSHD_AUTH_SUCCESS; + struct passwd* pwInfo; #ifdef WOLFSSH_OSSH_CERTS if (pubKeyCtx->isOsshCert) { + int rc; byte* caKey = NULL; word32 caKeySz; const byte* caKeyType = NULL; @@ -584,80 +667,353 @@ static int CheckPublicKeyUnix(const char* name, } if (ret == WSSHD_AUTH_SUCCESS) { - WMEMSET(authKeysPath, 0, sizeof(authKeysPath)); - rc = ResolveAuthKeysPath(pwInfo->pw_dir, authKeysPath); - if (rc != WS_SUCCESS) { - wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failed to resolve authorized keys" - " file path."); - ret = rc; - } + ret = SearchForPubKey(pwInfo->pw_dir, pubKeyCtx); } - if (ret == WSSHD_AUTH_SUCCESS) { - f = XFOPEN(authKeysPath, "rb"); - if (f == XBADFILE) { - wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Unable to open %s", - authKeysPath); - ret = WS_BAD_FILE_E; - } + } + + WOLFSSH_UNUSED(usrCaKeysFile); + WOLFSSH_UNUSED(authCtx); + return ret; +} +#endif /* !_WIN32*/ + +#ifdef _WIN32 + +#include +#include +#include + +#include +#include +#define MAX_USERNAME 256 + +static int _GetHomeDirectory(WOLFSSHD_AUTH* auth, const char* usr, WCHAR* out, int outSz) +{ + int ret = WS_SUCCESS; + WCHAR usrW[MAX_USERNAME]; + wchar_t* homeDir; + HRESULT hr; + size_t wr; + + /* convert user name to Windows wchar type */ + mbstowcs_s(&wr, usrW, MAX_USERNAME, usr, MAX_USERNAME-1); + + hr = SHGetKnownFolderPath((REFKNOWNFOLDERID)&FOLDERID_Profile, + 0, wolfSSHD_GetAuthToken(auth), &homeDir); + if (SUCCEEDED(hr)) { + wcscpy_s(out, outSz, homeDir); + CoTaskMemFree(homeDir); + } + else { + PROFILEINFO pInfo = { sizeof(PROFILEINFO) }; + + /* failed with get known folder path, try with loading the user profile */ + pInfo.dwFlags = PI_NOUI; + pInfo.lpUserName = usrW; + if (LoadUserProfileW(wolfSSHD_GetAuthToken(auth), &pInfo) != TRUE) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Error %d loading user %s", GetLastError(), usr); + ret = WS_FATAL_ERROR; } - if (ret == WSSHD_AUTH_SUCCESS) { - lineBuf = (char*)WMALLOC(MAX_LINE_SZ, NULL, DYNTYPE_BUFFER); - if (lineBuf == NULL) { - ret = WS_MEMORY_E; - } + + /* get home directory env. for user */ + if (ret == WS_SUCCESS && + ExpandEnvironmentStringsW(L"%USERPROFILE%", out, outSz) == 0) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Error getting user %s's home path", usr); + ret = WS_FATAL_ERROR; } - 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; - } + /* @TODO is unload of user needed here? + UnloadUserProfileW(wolfSSHD_GetAuthToken(conn->auth), pInfo.hProfile); + */ + } - if (currentSz <= 1) { - continue; /* empty line */ - } + return ret; +} - if (current[0] == '#') { - continue; /* commented out line */ - } - rc = CheckAuthKeysLine(current, currentSz, pubKeyCtx->publicKey, - pubKeyCtx->publicKeySz); - if (rc == WSSHD_AUTH_SUCCESS) { - foundKey = 1; - break; - } - else if (rc < 0) { - ret = rc; - break; - } +int wolfSSHD_GetHomeDirectory(WOLFSSHD_AUTH* auth, WOLFSSH* ssh, WCHAR* out, int outSz) +{ + return _GetHomeDirectory(auth, wolfSSH_GetUsername(ssh), out, outSz); +} + + +/* Returns the users token from LogonUserW call */ +HANDLE wolfSSHD_GetAuthToken(const WOLFSSHD_AUTH* auth) +{ + if (auth == NULL) + return NULL; + return auth->token; +} + +static int CheckPasswordWIN(const char* usr, const byte* pw, word32 pwSz, WOLFSSHD_AUTH* authCtx) +{ + int ret; + WCHAR* usrW = NULL; + WCHAR* pwW = NULL; + WCHAR dmW[] = L"."; /* currently hard set to use local domain */ + size_t usrWSz = 0; + int pwWSz = 0; + + wolfSSH_Log(WS_LOG_INFO, "[SSHD] Windows check password"); + + ret = WSSHD_AUTH_SUCCESS; + + usrWSz = WSTRLEN(usr) * sizeof(WCHAR); + + usrW = (WCHAR*)WMALLOC(usrWSz + 1, authCtx->heap, DYNTYPE_SSHD); + if (usrW == NULL) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Ran out of memory"); + ret = WSSHD_AUTH_FAILURE; + } + + if (ret == WSSHD_AUTH_SUCCESS) { + size_t wr = 0; + if (mbstowcs_s(&wr, usrW, usrWSz, usr, usrWSz-1) != 0) { + ret = WSSHD_AUTH_FAILURE; } } - if (f != XBADFILE) { - XFCLOSE(f); + if (ret == WSSHD_AUTH_SUCCESS) { + pwWSz = MultiByteToWideChar(CP_UTF8, 0, pw, pwSz, NULL, 0); + if (pwWSz <= 0) { + ret = WSSHD_AUTH_FAILURE; + } } - if (ret == WSSHD_AUTH_SUCCESS && !foundKey) { - ret = WSSHD_AUTH_FAILURE; + if (ret == WSSHD_AUTH_SUCCESS) { + pwW = (WCHAR*)WMALLOC((pwWSz * sizeof(WCHAR)) + sizeof(WCHAR), authCtx->heap, DYNTYPE_SSHD); + if (pwW == NULL) { + ret = WSSHD_AUTH_FAILURE; + } + } + + if (ret == WSSHD_AUTH_SUCCESS) { + if (MultiByteToWideChar(CP_UTF8, 0, pw, pwSz, pwW, pwWSz) != pwSz) { + ret = WSSHD_AUTH_FAILURE; + } + else { + pwW[pwWSz] = L'\0'; + } + } + + if (ret == WSSHD_AUTH_SUCCESS) { + if (LogonUserExExW(usrW, dmW, pwW, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, NULL, + &authCtx->token, NULL, NULL, NULL, NULL) != TRUE) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Windows failed with error %d when login in as user %s, " + "bad username or password", GetLastError(), usr); + wolfSSH_Log(WS_LOG_INFO, "[SSHD] Check user is allowed to 'Log on as batch job'"); + ret = WSSHD_AUTH_FAILURE; + } } - if (lineBuf != NULL) { - WFREE(lineBuf, NULL, DYNTYPE_BUFFER); + if (usrW != NULL) { + WFREE(usrW, authCtx->heap, DYNTYPE_SSHD); } - if (authKeysFile != NULL) { - WFREE(authKeysFile, NULL, DYNTYPE_STRING); + + if (pwW != NULL) { + WFREE(pwW, authCtx->heap, DYNTYPE_SSHD); } - (void)usrCaKeysFile; return ret; } -#endif /* !_WIN32*/ +static int CheckUserWIN(const char* name) +{ + int ret = WSSHD_AUTH_FAILURE; + + wolfSSH_Log(WS_LOG_INFO, "[SSHD] Windows user check happens with password/public key check"); + + ret = WSSHD_AUTH_SUCCESS; + + return ret; +} + + +/* Helper function to setup the user token in cases where public key + * auth is used. Return WSSHD_AUTH_SUCCESS on success */ +static int SetupUserTokenWin(const char* usr, + const WS_UserAuthData_PublicKey* pubKeyCtx, + const char* usrCaKeysFile, WOLFSSHD_AUTH* authCtx) +{ + int ret; + WCHAR* usrW = NULL; + WCHAR dmW[] = L"."; /* currently hard set to use local domain */ + ULONG rc; + HANDLE lsaHandle = NULL; + ULONG authId = 0; + void* authInfo = NULL; + ULONG authInfoSz = 0; + TOKEN_SOURCE sourceContext; + + size_t usrWSz; + + wolfSSH_Log(WS_LOG_INFO, "[SSHD] Windows public key get user token"); + + ret = WSSHD_AUTH_SUCCESS; + + usrWSz = WSTRLEN(usr); + usrW = (WCHAR*)WMALLOC((usrWSz + 1) * sizeof(WCHAR), NULL, DYNTYPE_SSHD); + if (usrW == NULL) { + ret = WS_MEMORY_E; + } + + if (ret == WSSHD_AUTH_SUCCESS) { + size_t wr; + if (mbstowcs_s(&wr, usrW, usrWSz + 1, usr, usrWSz) != 0) { + ret = WSSHD_AUTH_FAILURE; + } + } + + if (ret == WSSHD_AUTH_SUCCESS) { + LSA_OPERATIONAL_MODE oMode; + LSA_STRING processName; + + WMEMSET(&processName, 0, sizeof(LSA_STRING)); + processName.Buffer = "wolfsshd"; + processName.Length = (USHORT)WSTRLEN("wolfsshd"); + processName.MaximumLength = processName.Length + 1; + + + if ((rc = LsaRegisterLogonProcess(&processName, &lsaHandle, &oMode)) != STATUS_SUCCESS) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] LSA Register Logon Process Error %d", LsaNtStatusToWinError(rc)); + ret = WSSHD_AUTH_FAILURE; + } + } + + if (ret == WSSHD_AUTH_SUCCESS) { + LSA_STRING authName; + + WMEMSET(&authName, 0, sizeof(LSA_STRING)); + authName.Buffer = MSV1_0_PACKAGE_NAME; + authName.Length = (USHORT)WSTRLEN(MSV1_0_PACKAGE_NAME); + authName.MaximumLength = authName.Length + 1; + if (rc = LsaLookupAuthenticationPackage(lsaHandle, &authName, &authId) != STATUS_SUCCESS) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] LSA Lookup Authentication Package Error %d", rc); + ret = WSSHD_AUTH_FAILURE; + } + } + + if (ret == WSSHD_AUTH_SUCCESS) { + /* size of logon struct plus computer and user name */ + authInfoSz = (ULONG)(sizeof(MSV1_0_S4U_LOGON) + + (wcslen(usrW) + wcslen(dmW)) * sizeof(wchar_t)); + authInfo = (void*)WMALLOC(authInfoSz, NULL, DYNTYPE_SSHD); + if (authInfo == NULL) { + ret = WSSHD_AUTH_FAILURE; + } + else { + MSV1_0_S4U_LOGON* l; + + WMEMSET(authInfo, 0, authInfoSz); + l = (MSV1_0_S4U_LOGON*)authInfo; + l->MessageType = MsV1_0S4ULogon; + + /* write user name after the MSV1_0_S4U_LOGON structure in buffer */ + l->UserPrincipalName.Length = (USHORT)(wcslen(usrW) * sizeof(wchar_t)); + l->UserPrincipalName.MaximumLength = l->UserPrincipalName.Length; + l->UserPrincipalName.Buffer = (WCHAR*)((byte*)l + sizeof(MSV1_0_S4U_LOGON)); + memcpy_s(l->UserPrincipalName.Buffer, l->UserPrincipalName.Length, usrW, l->UserPrincipalName.Length); + + /* write domain name after the user name in buffer */ + l->DomainName.Length = (USHORT)(wcslen(dmW) * sizeof(wchar_t)); + l->DomainName.MaximumLength = l->UserPrincipalName.Length; + l->DomainName.Buffer = (WCHAR*)((byte*)(l->UserPrincipalName.Buffer) + l->UserPrincipalName.Length); + memcpy_s(l->DomainName.Buffer, l->DomainName.Length, dmW, l->DomainName.Length); + } + } + + if (ret == WSSHD_AUTH_SUCCESS) { + strcpy_s(sourceContext.SourceName, TOKEN_SOURCE_LENGTH, "sshd"); + if (AllocateLocallyUniqueId(&sourceContext.SourceIdentifier) != TRUE) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Windows failed to allocate locally unique source context id"); + ret = WSSHD_AUTH_FAILURE; + } + } + + if (ret == WSSHD_AUTH_SUCCESS) { + LSA_STRING originName; + NTSTATUS subStatus; + QUOTA_LIMITS quotas; + DWORD profileSz; + PKERB_INTERACTIVE_PROFILE profile = NULL; + LUID logonId = { 0, 0 }; + + WMEMSET(&originName, 0, sizeof(LSA_STRING)); + originName.Buffer = "wolfsshd"; + originName.Length = (USHORT)WSTRLEN("wolfsshd"); + originName.MaximumLength = originName.Length + 1; + + if ((rc = LsaLogonUser(lsaHandle, &originName, Network, authId, authInfo, authInfoSz, NULL, &sourceContext, &profile, &profileSz, &logonId, &authCtx->token, "as, &subStatus)) != STATUS_SUCCESS) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Windows failed with status %X, SubStatus %d, when login in as user %s", + rc, subStatus, usr); + ret = WSSHD_AUTH_FAILURE; + } + + /* currently not using the profile returned, free it here */ + if (profile != NULL) { + LsaFreeReturnBuffer(profile); + } + } + + if (authInfo != NULL) { + WFREE(authInfo, NULL, DYNTYPE_SSHD); + } + + if (lsaHandle != NULL) { + LsaDeregisterLogonProcess(lsaHandle); + } + + return ret; +} + +/* Uses Windows LSA for getting an impersination token */ +static int CheckPublicKeyWIN(const char* usr, + const WS_UserAuthData_PublicKey* pubKeyCtx, + const char* usrCaKeysFile, WOLFSSHD_AUTH* authCtx) +{ + int ret; + + wolfSSH_Log(WS_LOG_INFO, "[SSHD] Windows check public key"); + + ret = SetupUserTokenWin(usr, pubKeyCtx,usrCaKeysFile, authCtx); + + /* after successful logon check the public key sent */ + if (ret == WSSHD_AUTH_SUCCESS) { + WCHAR h[MAX_PATH]; + + if (_GetHomeDirectory(authCtx, usr, h, MAX_PATH) == WS_SUCCESS) { + CHAR r[MAX_PATH]; + size_t rSz; + + if (wcstombs_s(&rSz, r, MAX_PATH, h, MAX_PATH - 1) != 0) { + ret = WSSHD_AUTH_FAILURE; + } + + if (ret == WSSHD_AUTH_SUCCESS) { + r[rSz-1] = L'\0'; + + ret = SearchForPubKey(r, pubKeyCtx); + if (ret != WSSHD_AUTH_SUCCESS) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Failed to find public key for user %s", usr); + ret = WSSHD_AUTH_FAILURE; + } + } + } + else { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Windows failed to get home directory for user %s", usr); + ret = WSSHD_AUTH_FAILURE; + } + } + + return ret; +} +#endif /* _WIN32*/ + /* return WOLFSSH_USERAUTH_SUCCESS on success */ static int DoCheckUser(const char* usr, WOLFSSHD_AUTH* auth) { @@ -734,7 +1090,7 @@ static int RequestAuthentication(WS_UserAuthData* authData, } else { rc = authCtx->checkPasswordCb(usr, authData->sf.password.password, - authData->sf.password.passwordSz); + authData->sf.password.passwordSz, authCtx); if (rc == WSSHD_AUTH_SUCCESS) { wolfSSH_Log(WS_LOG_INFO, "[SSHD] Password ok."); } @@ -832,22 +1188,40 @@ static int RequestAuthentication(WS_UserAuthData* authData, !wolfSSHD_ConfigGetAuthKeysFileSet(authCtx->conf)) { wolfSSH_Log(WS_LOG_INFO, "[SSHD] Relying on CA for public key check"); + #ifdef WIN32 + /* Still need to get users token on Windows */ + rc = SetupUserTokenWin(usr, &authData->sf.publicKey, + wolfSSHD_ConfigGetUserCAKeysFile(authCtx->conf), authCtx); + if (rc == WSSHD_AUTH_SUCCESS) { + wolfSSH_Log(WS_LOG_INFO, "[SSHD] Got users token ok."); + ret = WOLFSSH_USERAUTH_SUCCESS; + } + else { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Error getting users token."); + ret = WOLFSSH_USERAUTH_FAILURE; + } + #else ret = WOLFSSH_USERAUTH_SUCCESS; + #endif } else { /* if not a certificate then parse through authorized key file */ rc = authCtx->checkPublicKeyCb(usr, &authData->sf.publicKey, - wolfSSHD_ConfigGetUserCAKeysFile(authCtx->conf)); + wolfSSHD_ConfigGetUserCAKeysFile(authCtx->conf), + authCtx); if (rc == WSSHD_AUTH_SUCCESS) { wolfSSH_Log(WS_LOG_INFO, "[SSHD] Public key ok."); ret = WOLFSSH_USERAUTH_SUCCESS; } else if (rc == WSSHD_AUTH_FAILURE) { - wolfSSH_Log(WS_LOG_INFO, "[SSHD] Public key not authorized."); + wolfSSH_Log(WS_LOG_INFO, + "[SSHD] Public key not authorized."); ret = WOLFSSH_USERAUTH_INVALID_PUBLICKEY; } else { - wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error checking public key."); + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Error checking public key."); ret = WOLFSSH_USERAUTH_FAILURE; } } @@ -939,7 +1313,9 @@ static int SetDefaultUserCheck(WOLFSSHD_AUTH* auth) int ret = WS_NOT_COMPILED; #ifdef _WIN32 - /* TODO: Implement for Windows. */ + wolfSSH_Log(WS_LOG_INFO, "[SSHD] Setting default Windows user name check"); + auth->checkUserCb = CheckUserWIN; + ret = WS_SUCCESS; #else wolfSSH_Log(WS_LOG_INFO, "[SSHD] Setting default Unix user name check"); auth->checkUserCb = CheckUserUnix; @@ -955,7 +1331,9 @@ static int SetDefaultPasswordCheck(WOLFSSHD_AUTH* auth) int ret = WS_NOT_COMPILED; #ifdef _WIN32 - /* TODO: Add CheckPasswordWin. */ + wolfSSH_Log(WS_LOG_INFO, "[SSHD] Setting Windows password check"); + auth->checkPasswordCb = CheckPasswordWIN; + ret = WS_SUCCESS; #elif defined(WOLFSSH_USE_PAM) wolfSSH_Log(WS_LOG_INFO, "[SSHD] Setting PAM password check"); auth->checkPasswordCb = CheckPasswordPAM; @@ -974,7 +1352,9 @@ static int SetDefaultPublicKeyCheck(WOLFSSHD_AUTH* auth) int ret = WS_NOT_COMPILED; #ifdef _WIN32 - /* TODO: Implement for Windows. */ + wolfSSH_Log(WS_LOG_INFO, "[SSHD] Setting Windows public key check"); + auth->checkPublicKeyCb = CheckPublicKeyWIN; + ret = WS_SUCCESS; #else wolfSSH_Log(WS_LOG_INFO, "[SSHD] Setting Unix public key check"); auth->checkPublicKeyCb = CheckPublicKeyUnix; @@ -983,6 +1363,33 @@ static int SetDefaultPublicKeyCheck(WOLFSSHD_AUTH* auth) return ret; } +static int SetDefualtUserID(WOLFSSHD_AUTH* auth) +{ +#ifdef _WIN32 + /* TODO: Implement for Windows. */ + return 0; +#else + struct passwd* pwInfo; + const char* usr = "sshd"; + int ret = WS_SUCCESS; + + pwInfo = getpwnam(usr); + if (pwInfo == NULL) { + /* user name not found on system */ + wolfSSH_Log(WS_LOG_INFO, "[SSHD] No sshd user found to use"); + ret = WS_FATAL_ERROR; + } + + if (ret == WS_SUCCESS) { + auth->gid = pwInfo->pw_gid; + auth->uid = pwInfo->pw_uid; + auth->sGid = getgid(); + auth->sUid = getuid(); + } + return ret; +#endif +} + /* Sets the default functions to be used for authentication of peer. * Later the default functions could be overriden if needed. @@ -994,8 +1401,6 @@ WOLFSSHD_AUTH* wolfSSHD_AuthCreateUser(void* heap, const WOLFSSHD_CONFIG* conf) auth = (WOLFSSHD_AUTH*)WMALLOC(sizeof(WOLFSSHD_AUTH), heap, DYNTYPE_SSHD); if (auth != NULL) { int ret; - struct passwd* pwInfo; - const char* usr = "sshd"; auth->heap = heap; auth->conf = conf; @@ -1004,7 +1409,8 @@ WOLFSSHD_AUTH* wolfSSHD_AuthCreateUser(void* heap, const WOLFSSHD_CONFIG* conf) /* set the default user checking based on build */ ret = SetDefaultUserCheck(auth); if (ret != WS_SUCCESS) { - wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error setting default user check."); + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Error setting default user check."); } /* set the default password checking based on build */ @@ -1026,21 +1432,13 @@ WOLFSSHD_AUTH* wolfSSHD_AuthCreateUser(void* heap, const WOLFSSHD_CONFIG* conf) } if (ret == WS_SUCCESS) { - pwInfo = getpwnam(usr); - if (pwInfo == NULL) { - /* user name not found on system */ - wolfSSH_Log(WS_LOG_INFO, "[SSHD] No sshd user found to use"); - ret = WS_FATAL_ERROR; + ret = SetDefualtUserID(auth); + if (ret != WS_SUCCESS) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error setting default " + "user ID."); } } - if (ret == WS_SUCCESS) { - auth->gid = pwInfo->pw_gid; - auth->uid = pwInfo->pw_uid; - auth->sGid = getgid(); - auth->sUid = getuid(); - } - /* error case in setting one of the default callbacks */ if (ret != WS_SUCCESS) { (void)wolfSSHD_AuthFreeUser(auth); @@ -1069,6 +1467,7 @@ int wolfSSHD_AuthRaisePermissions(WOLFSSHD_AUTH* auth) int ret = 0; wolfSSH_Log(WS_LOG_INFO, "[SSHD] Attempting to raise permissions level"); +#ifndef WIN32 if (auth) { if (setegid(auth->sGid) != 0) { wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error rasing gid"); @@ -1083,6 +1482,7 @@ int wolfSSHD_AuthRaisePermissions(WOLFSSHD_AUTH* auth) else { ret = WS_BAD_ARGUMENT; } +#endif return ret; } @@ -1092,6 +1492,7 @@ int wolfSSHD_AuthRaisePermissions(WOLFSSHD_AUTH* auth) int wolfSSHD_AuthReducePermissionsUser(WOLFSSHD_AUTH* auth, WUID_T uid, WGID_T gid) { +#ifndef WIN32 if (setregid(gid, gid) != 0) { wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error setting user gid"); return WS_FATAL_ERROR; @@ -1101,6 +1502,7 @@ int wolfSSHD_AuthReducePermissionsUser(WOLFSSHD_AUTH* auth, WUID_T uid, wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error setting user uid"); return WS_FATAL_ERROR; } +#endif (void)auth; return WS_SUCCESS; } @@ -1113,6 +1515,7 @@ int wolfSSHD_AuthReducePermissions(WOLFSSHD_AUTH* auth) int ret = WS_SUCCESS; flag = wolfSSHD_ConfigGetPrivilegeSeparation(auth->conf); +#ifndef WIN32 if (flag == WOLFSSHD_PRIV_SEPARAT || flag == WOLFSSHD_PRIV_SANDBOX) { wolfSSH_Log(WS_LOG_INFO, "[SSHD] Lowering permissions level"); if (auth) { @@ -1130,23 +1533,26 @@ int wolfSSHD_AuthReducePermissions(WOLFSSHD_AUTH* auth) ret = WS_BAD_ARGUMENT; } } +#endif return ret; } - +#ifndef WIN32 #if defined(__OSX__) || defined( __APPLE__) #define WGETGROUPLIST(x,y,z,w) getgrouplist((x),(y),(int*)(z),(w)) #else #define WGETGROUPLIST(x,y,z,w) getgrouplist((x),(y),(z),(w)) #endif +#endif /* WIN32 */ /* sets the extended groups the user is in, returns WS_SUCCESS on success */ int wolfSSHD_AuthSetGroups(const WOLFSSHD_AUTH* auth, const char* usr, WGID_T gid) { + int ret = WS_SUCCESS; +#ifndef WIN32 int grpListSz = 0; gid_t* grpList = NULL; - int ret = WS_SUCCESS; #if defined(__QNX__) || defined(__QNXNTO__) /* QNX does not support getting the exact group list size ahead of time, @@ -1183,7 +1589,11 @@ int wolfSSHD_AuthSetGroups(const WOLFSSHD_AUTH* auth, const char* usr, WFREE(grpList, auth->heap, DYNTYPE_SSHD); } } - +#else + WOLFSSH_UNUSED(auth); + WOLFSSH_UNUSED(usr); + WOLFSSH_UNUSED(gid); +#endif return ret; } @@ -1207,14 +1617,18 @@ WOLFSSHD_CONFIG* wolfSSHD_AuthGetUserConf(const WOLFSSHD_AUTH* auth, const char* localAdr, word16* localPort, const char* RDomain, const char* adr) { - struct group* g = NULL; WOLFSSHD_CONFIG* ret = NULL; if (auth != NULL) { - struct passwd *p_passwd; char* gName = NULL; if (usr != NULL) { +#ifdef WIN32 + //LogonUserEx() +#else + struct passwd* p_passwd; + struct group* g = NULL; + p_passwd = getpwnam((const char *)usr); if (p_passwd == NULL) { return NULL; @@ -1225,6 +1639,7 @@ WOLFSSHD_CONFIG* wolfSSHD_AuthGetUserConf(const WOLFSSHD_AUTH* auth, return NULL; } gName = g->gr_name; +#endif } ret = wolfSSHD_GetUserConf(auth->conf, usr, gName, host, localAdr, diff --git a/apps/wolfsshd/auth.h b/apps/wolfsshd/auth.h index fc868576f..ddc6e90aa 100644 --- a/apps/wolfsshd/auth.h +++ b/apps/wolfsshd/auth.h @@ -46,7 +46,7 @@ typedef int (*CallbackCheckUser)(const char* usr); * found, and negative values if an error occurs during checking. */ typedef int (*CallbackCheckPassword)(const char* usr, const byte* psw, - word32 pswSz); + word32 pswSz, WOLFSSHD_AUTH* authCtx); /* * Returns WSSHD_AUTH_SUCCESS if public key ok, WSSHD_AUTH_FAILURE if key not @@ -54,7 +54,7 @@ typedef int (*CallbackCheckPassword)(const char* usr, const byte* psw, */ typedef int (*CallbackCheckPublicKey)(const char* usr, const WS_UserAuthData_PublicKey* pubKey, - const char* usrCaKeysFile); + const char* usrCaKeysFile, WOLFSSHD_AUTH* authCtx); WOLFSSHD_AUTH* wolfSSHD_AuthCreateUser(void* heap, const WOLFSSHD_CONFIG* conf); int wolfSSHD_AuthFreeUser(WOLFSSHD_AUTH* auth); @@ -69,4 +69,8 @@ WOLFSSHD_CONFIG* wolfSSHD_AuthGetUserConf(const WOLFSSHD_AUTH* auth, const char* usr, const char* host, const char* localAdr, word16* localPort, const char* RDomain, const char* adr); +#ifdef _WIN32 +HANDLE wolfSSHD_GetAuthToken(const WOLFSSHD_AUTH* auth); +int wolfSSHD_GetHomeDirectory(WOLFSSHD_AUTH* auth, WOLFSSH* ssh, WCHAR* out, int outSz); +#endif #endif /* WOLFAUTH_H */ diff --git a/apps/wolfsshd/configuration.c b/apps/wolfsshd/configuration.c index 71d2d5de6..33f2b6ef1 100644 --- a/apps/wolfsshd/configuration.c +++ b/apps/wolfsshd/configuration.c @@ -22,6 +22,12 @@ #include #endif +#ifdef WOLFSSL_USER_SETTINGS + #include +#else + #include +#endif + #ifdef WOLFSSH_SSHD /* functions for parsing out options from a config file and for handling loading * key/certs using the env. filesystem */ @@ -47,7 +53,10 @@ #endif #include "configuration.h" + +#ifndef WIN32 #include +#endif struct WOLFSSHD_CONFIG { void* heap; @@ -64,6 +73,7 @@ struct WOLFSSHD_CONFIG { char* listenAddress; char* authKeysFile; char* forceCmd; + char* pidFile; WOLFSSHD_CONFIG* next; /* next config in list */ long loginTimer; word16 port; @@ -76,6 +86,7 @@ struct WOLFSSHD_CONFIG { }; int CountWhitespace(const char* in, int inSz, byte inv); +int SetFileString(char** dst, const char* src, void* heap); /* convert a string into seconds, handles if 'm' for minutes follows the string * number, i.e. 2m @@ -294,6 +305,7 @@ void wolfSSHD_ConfigFree(WOLFSSHD_CONFIG* conf) FreeString(¤t->authKeysFile, heap); FreeString(¤t->hostKeyFile, heap); FreeString(¤t->hostCertFile, heap); + FreeString(¤t->pidFile, heap); WFREE(current, heap, DYNTYPE_SSHD); current = next; @@ -330,9 +342,10 @@ enum { OPT_FORCE_CMD = 19, OPT_HOST_CERT = 20, OPT_TRUSTED_USER_CA_KEYS = 21, + OPT_PIDFILE = 22, }; enum { - NUM_OPTIONS = 22 + NUM_OPTIONS = 23 }; static const CONFIG_OPTION options[NUM_OPTIONS] = { @@ -358,6 +371,7 @@ static const CONFIG_OPTION options[NUM_OPTIONS] = { {OPT_FORCE_CMD, "ForceCommand"}, {OPT_HOST_CERT, "HostCertificate"}, {OPT_TRUSTED_USER_CA_KEYS, "TrustedUserCAKeys"}, + {OPT_PIDFILE, "PidFile"}, }; /* returns WS_SUCCESS on success */ @@ -658,7 +672,7 @@ static int HandleInclude(WOLFSSHD_CONFIG *conf, const char *value) char** fileNames = NULL; /* Count up the number of files */ - while ((dir = WREADDIR(&d)) != NULL) { + while ((dir = WREADDIR(NULL, &d)) != NULL) { /* Skip sub-directories */ #if defined(__QNX__) || defined(__QNXNTO__) struct stat s; @@ -672,7 +686,7 @@ static int HandleInclude(WOLFSSHD_CONFIG *conf, const char *value) fileCount++; } } - WREWINDDIR(&d); + WREWINDDIR(NULL, &d); if (fileCount > 0) { fileNames = (char**)WMALLOC(fileCount * sizeof(char*), @@ -684,7 +698,7 @@ static int HandleInclude(WOLFSSHD_CONFIG *conf, const char *value) if (ret == WS_SUCCESS) { i = 0; - while (i < fileCount && (dir = WREADDIR(&d)) != NULL) { + while (i < fileCount && (dir = WREADDIR(NULL, &d)) != NULL) { /* Skip sub-directories */ #if defined(__QNX__) || defined(__QNXNTO__) struct stat s; @@ -752,7 +766,7 @@ static int HandleInclude(WOLFSSHD_CONFIG *conf, const char *value) WFREE(fileNames, conf->heap, DYNTYPE_PATH); } } - WCLOSEDIR(&d); + WCLOSEDIR(NULL, &d); } else { /* Bad directory */ @@ -849,7 +863,7 @@ static int AddRestrictedCase(WOLFSSHD_CONFIG* config, const char* mtch, * and makes it point to the newly created conf node */ static int HandleMatch(WOLFSSHD_CONFIG** conf, const char* value, int valueSz) { - WOLFSSHD_CONFIG* newConf; + WOLFSSHD_CONFIG* newConf = NULL; int ret = WS_SUCCESS; if (conf == NULL || *conf == NULL || value == NULL) { @@ -999,6 +1013,9 @@ static int HandleConfigOption(WOLFSSHD_CONFIG** conf, int opt, /* TODO: Add logic to check if file exists? */ ret = wolfSSHD_ConfigSetUserCAKeysFile(*conf, value); break; + case OPT_PIDFILE: + ret = SetFileString(&(*conf)->pidFile, value, (*conf)->heap); + break; default: break; } @@ -1070,8 +1087,13 @@ WOLFSSHD_STATIC int ParseConfigLine(WOLFSSHD_CONFIG** conf, const char* l, } } else { + #ifdef WOLFSSH_IGNORE_UNKNOWN_CONFIG + wolfSSH_Log(WS_LOG_DEBUG, "[SSHD] ignoring config line %s.", l); + ret = WS_SUCCESS; + #else wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Error parsing config line."); ret = WS_FATAL_ERROR; + #endif } return ret; @@ -1083,7 +1105,7 @@ WOLFSSHD_STATIC int ParseConfigLine(WOLFSSHD_CONFIG** conf, const char* l, */ int wolfSSHD_ConfigLoad(WOLFSSHD_CONFIG* conf, const char* filename) { - XFILE f; + WFILE *f; WOLFSSHD_CONFIG* currentConfig; int ret = WS_SUCCESS; char buf[MAX_LINE_SIZE]; @@ -1092,8 +1114,7 @@ int wolfSSHD_ConfigLoad(WOLFSSHD_CONFIG* conf, const char* filename) if (conf == NULL || filename == NULL) return BAD_FUNC_ARG; - f = XFOPEN(filename, "rb"); - if (f == XBADFILE) { + if (WFOPEN(NULL, &f, filename, "rb") != 0) { wolfSSH_Log(WS_LOG_ERROR, "Unable to open SSHD config file %s", filename); return BAD_FUNC_ARG; @@ -1111,7 +1132,7 @@ int wolfSSHD_ConfigLoad(WOLFSSHD_CONFIG* conf, const char* filename) current = current + 1; } - if (currentSz <= 1) { + if (currentSz <= 2) { /* \n or \r\n */ continue; /* empty line */ } @@ -1125,7 +1146,7 @@ int wolfSSHD_ConfigLoad(WOLFSSHD_CONFIG* conf, const char* filename) break; } } - XFCLOSE(f); + WFCLOSE(NULL, f); SetAuthKeysPattern(conf->authKeysFile); @@ -1288,7 +1309,7 @@ char* wolfSSHD_ConfigGetUserCAKeysFile(const WOLFSSHD_CONFIG* conf) return ret; } -static int SetFileString(char** dst, const char* src, void* heap) +int SetFileString(char** dst, const char* src, void* heap) { int ret = WS_SUCCESS; @@ -1420,4 +1441,20 @@ long wolfSSHD_ConfigGetGraceTime(const WOLFSSHD_CONFIG* conf) return ret; } + + +/* Used to save out the PID of SSHD to a file */ +void wolfSSHD_ConfigSavePID(const WOLFSSHD_CONFIG* conf) +{ + FILE* f; + char buf[12]; /* large enough to hold 'int' type with null terminator */ + + WMEMSET(buf, 0, sizeof(buf)); + if (WFOPEN(NULL, &f, conf->pidFile, "wb") == 0) { + WSNPRINTF(buf, sizeof(buf), "%d", getpid()); + WFWRITE(NULL, buf, 1, WSTRLEN(buf), f); + WFCLOSE(NULL, f); + } +} + #endif /* WOLFSSH_SSHD */ diff --git a/apps/wolfsshd/configuration.h b/apps/wolfsshd/configuration.h index 9d68d0b31..68807975d 100644 --- a/apps/wolfsshd/configuration.h +++ b/apps/wolfsshd/configuration.h @@ -56,6 +56,7 @@ WOLFSSHD_CONFIG* wolfSSHD_GetUserConf(const WOLFSSHD_CONFIG* conf, const char* usr, const char* grp, const char* host, const char* localAdr, word16* localPort, const char* RDomain, const char* adr); +void wolfSSHD_ConfigSavePID(const WOLFSSHD_CONFIG* conf); #ifdef WOLFSSHD_UNIT_TEST int ParseConfigLine(WOLFSSHD_CONFIG** conf, const char* l, int lSz); diff --git a/apps/wolfsshd/test/test_configuration.c b/apps/wolfsshd/test/test_configuration.c index 1528731ec..4c1000e23 100644 --- a/apps/wolfsshd/test/test_configuration.c +++ b/apps/wolfsshd/test/test_configuration.c @@ -33,7 +33,7 @@ static void CleanupWildcardTest(void) char filepath[MAX_PATH*2]; /* d_name is max_path long */ if (!WOPENDIR(NULL, NULL, &dir, "./sshd_config.d/")) { - while ((d = WREADDIR(&dir)) != NULL) { + while ((d = WREADDIR(NULL, &dir)) != NULL) { #if defined(__QNX__) || defined(__QNXNTO__) struct stat s; @@ -48,7 +48,7 @@ static void CleanupWildcardTest(void) WREMOVE(0, filepath); } } - WCLOSEDIR(&dir); + WCLOSEDIR(NULL, &dir); WRMDIR(0, "./sshd_config.d/"); } } @@ -75,15 +75,15 @@ static int SetupWildcardTest(void) "./sshd_config.d/"); } - WFOPEN(&f, filepath, "w"); + WFOPEN(NULL, &f, filepath, "w"); if (f) { word32 sz, wr; char contents[20]; WSNPRINTF(contents, sizeof contents, "LoginGraceTime %02u", fileIds[i]); sz = (word32)WSTRLEN(contents); - wr = (word32)WFWRITE(contents, sizeof(char), sz, f); - WFCLOSE(f); + wr = (word32)WFWRITE(NULL, contents, sizeof(char), sz, f); + WFCLOSE(NULL, f); if (sz != wr) { Log("Couldn't write the contents of file %s\n", filepath); ret = WS_FATAL_ERROR; diff --git a/apps/wolfsshd/wolfsshd.c b/apps/wolfsshd/wolfsshd.c index 4991b62dc..dc8ed5eef 100644 --- a/apps/wolfsshd/wolfsshd.c +++ b/apps/wolfsshd/wolfsshd.c @@ -22,6 +22,12 @@ #include #endif +#ifdef WOLFSSL_USER_SETTINGS + #include +#else + #include +#endif + #ifdef WOLFSSH_SSHD #include @@ -51,7 +57,7 @@ #define WOLFSSHD_TIMEOUT 1 #endif -#ifdef WOLFSSH_SHELL +#if defined(WOLFSSH_SHELL) && !defined(_WIN32) #ifdef HAVE_PTY_H #include #endif @@ -80,18 +86,20 @@ static void ConnClose(int sig) { +#ifndef WIN32 pid_t p; int ret; p = wait(&ret); if (p == 0 || p == -1) return; /* parent or error state*/ (void)ret; +#endif (void)sig; } #endif /* WOLFSSH_SHELL */ static volatile byte debugMode = 0; /* default to off */ -static FILE* logFile = NULL; +static WFILE* logFile = NULL; /* catch interrupts and close down gracefully */ static volatile byte quit = 0; @@ -104,6 +112,7 @@ typedef struct WOLFSSHD_CONNECTION { int fd; int listenFd; char ip[INET_ADDRSTRLEN]; + byte isThreaded; } WOLFSSHD_CONNECTION; #ifdef __unix__ @@ -141,6 +150,29 @@ static void SyslogCb(enum wolfSSH_LogLevel level, const char *const msgStr) #endif +#ifdef _WIN32 +static void ServiceDebugCb(enum wolfSSH_LogLevel level, const char* const msgStr) +{ + WCHAR* wc; + size_t szWord = WSTRLEN(msgStr) + 3; /* + 3 for null terminator and new + * line */ + size_t sz = szWord *sizeof(wchar_t); + wc = (WCHAR*)WMALLOC(sz, NULL, DYNAMIC_TYPE_LOG); + if (wc) { + size_t con; + + if (mbstowcs_s(&con, wc, szWord, msgStr, szWord-1) == 0) { + wc[con - 1] = L'\r'; + wc[con] = L'\n'; + wc[con + 1] = L'\0'; + OutputDebugString(wc); + } + WFREE(wc, NULL, DYNAMIC_TYPE_LOG); + } + WOLFSSH_UNUSED(level); +} +#endif + static void ShowUsage(void) { printf("wolfsshd %s\n", LIBWOLFSSH_VERSION_STRING); @@ -164,16 +196,22 @@ static void interruptCatch(int in) quit = 1; } +#ifdef WIN32 + #include + #define WGETPID GetCurrentProcessId +#else + #define WGETPID getpid +#endif /* redirect logging to a specific file and add the PID value */ static void wolfSSHDLoggingCb(enum wolfSSH_LogLevel lvl, const char *const str) { /* always log errors and optionally log other info/debug level messages */ if (lvl == WS_LOG_ERROR) { - fprintf(logFile, "[PID %d]: %s\n", getpid(), str); + fprintf(logFile, "[PID %d]: %s\n", WGETPID(), str); } else if (debugMode) { - fprintf(logFile, "[PID %d]: %s\n", getpid(), str); + fprintf(logFile, "[PID %d]: %s\n", WGETPID(), str); } } @@ -207,22 +245,22 @@ static byte* getBufferFromFile(const char* fileName, word32* bufSz, void* heap) if (fileName == NULL) return NULL; - if (WFOPEN(&file, fileName, "rb") != 0) + if (WFOPEN(NULL, &file, fileName, "rb") != 0) return NULL; - WFSEEK(file, 0, XSEEK_END); - fileSz = (word32)WFTELL(file); - WREWIND(file); + WFSEEK(NULL, file, 0, XSEEK_END); + fileSz = (word32)WFTELL(NULL, file); + WREWIND(NULL, file); buf = (byte*)WMALLOC(fileSz + 1, heap, DYNTYPE_SSHD); if (buf != NULL) { - readSz = (word32)WFREAD(buf, 1, fileSz, file); + readSz = (word32)WFREAD(NULL, buf, 1, fileSz, file); if (readSz < fileSz) { - WFCLOSE(file); + WFCLOSE(NULL, file); WFREE(buf, heap, DYNTYPE_SSHD); return NULL; } *bufSz = readSz; - WFCLOSE(file); + WFCLOSE(NULL, file); } (void)heap; @@ -247,6 +285,10 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx) word32 privBufSz; void* heap = NULL; + if (ctx == NULL) { + return WS_BAD_ARGUMENT; + } + /* create a new WOLFSSH_CTX */ *ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL); if (ctx == NULL) { @@ -404,7 +446,10 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx) } } #endif - wolfSSH_SetUserAuthTypes(*ctx, DefaultUserAuthTypes); + + if (ret == WS_SUCCESS) { + wolfSSH_SetUserAuthTypes(*ctx, DefaultUserAuthTypes); + } /* @TODO Load in host public key */ @@ -414,7 +459,7 @@ static int SetupCTX(WOLFSSHD_CONFIG* conf, WOLFSSH_CTX** ctx) return ret; } - +#ifndef _WIN32 /* return 1 if set, 0 if not set and negative values on error */ static int SetupChroot(WOLFSSHD_CONFIG* usrConf) { @@ -444,6 +489,7 @@ static int SetupChroot(WOLFSSHD_CONFIG* usrConf) } return ret; } +#endif #ifdef WOLFSSH_SCP static int SCP_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, @@ -453,9 +499,11 @@ static int SCP_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, int error = WS_SUCCESS; int select_ret = 0; +#ifndef _WIN32 /* temporarily elevate permissions to get users information */ if (wolfSSHD_AuthRaisePermissions(conn->auth) != WS_SUCCESS) { - wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failure to raise permissions for auth"); + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Failure to raise permissions for auth"); return WS_FATAL_ERROR; } @@ -483,6 +531,14 @@ static int SCP_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, return WS_FATAL_ERROR; } +#else + /* impersonate the logged on user for file permissions */ + if (ImpersonateLoggedOnUser(wolfSSHD_GetAuthToken(conn->auth)) == FALSE) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Error impersonating logged on user"); + ret = WS_FATAL_ERROR; + } +#endif if (ret == WS_SUCCESS) { ret = wolfSSH_accept(ssh); @@ -512,6 +568,10 @@ static int SCP_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, } (void)conn; +#ifdef _WIN32 + /* stop impersonating the user */ + RevertToSelf(); +#endif return ret; } #endif /* WOLFSSH_SCP */ @@ -533,9 +593,11 @@ static int SFTP_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, int timeout = TEST_SFTP_TIMEOUT_NONE; byte peek_buf[1]; +#ifndef _WIN32 /* temporarily elevate permissions to get users information */ if (wolfSSHD_AuthRaisePermissions(conn->auth) != WS_SUCCESS) { - wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failure to raise permissions for auth"); + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Failure to raise permissions for auth"); return WS_FATAL_ERROR; } @@ -561,6 +623,7 @@ static int SFTP_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, } } + /* set starting SFTP directory */ if (ret == WS_SUCCESS) { WDIR dir; @@ -573,7 +636,7 @@ static int SFTP_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, "[SSHD] Error setting SFTP default home path"); ret = WS_FATAL_ERROR; } - WCLOSEDIR(&dir); + WCLOSEDIR(NULL, &dir); } } @@ -587,6 +650,38 @@ static int SFTP_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, return WS_FATAL_ERROR; } +#else + char r[MAX_PATH]; + size_t rSz = 0; + WCHAR h[MAX_PATH]; + + ret = wolfSSHD_GetHomeDirectory(conn->auth, ssh, h, MAX_PATH); + + /* convert home directory from wchar type to char */ + if (ret == WS_SUCCESS) { + if (wcstombs_s(&rSz, r, MAX_PATH, h, MAX_PATH - 1) != 0) { + ret = WS_FATAL_ERROR; + } + } + + if (ret == WS_SUCCESS) { + r[rSz] = '\0'; + wolfSSH_Log(WS_LOG_INFO, + "[SSHD] Using directory %s for SFTP connection", r); + if (wolfSSH_SFTP_SetDefaultPath(ssh, r) != WS_SUCCESS) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Error setting SFTP default home path"); + ret = WS_FATAL_ERROR; + } + } + + /* impersonate the logged on user for file permissions */ + if (ImpersonateLoggedOnUser(wolfSSHD_GetAuthToken(conn->auth)) == FALSE) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Error impersonating logged on user"); + ret = WS_FATAL_ERROR; + } +#endif if (ret == WS_SUCCESS) { sockfd = (WS_SOCKET_T)wolfSSH_get_fd(ssh); @@ -596,6 +691,21 @@ static int SFTP_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, break; } + if (wolfSSH_SFTP_PendingSend(ssh)) { + /* Yes, process the SFTP data. */ + ret = wolfSSH_SFTP_read(ssh); + error = wolfSSH_get_error(ssh); + timeout = (ret == WS_REKEYING) ? + TEST_SFTP_TIMEOUT : TEST_SFTP_TIMEOUT_NONE; + if (error == WS_WANT_READ || error == WS_WANT_WRITE || + error == WS_CHAN_RXD || error == WS_REKEYING || + error == WS_WINDOW_FULL) + ret = error; + if (error == WS_EOF) { + break; + } + } + if (ret == WS_WANT_READ || ret == WS_WANT_WRITE || select_ret == WS_SELECT_RECV_READY) { ret = wolfSSH_worker(ssh, NULL); @@ -622,22 +732,6 @@ static int SFTP_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, } } - if (wolfSSH_SFTP_PendingSend(ssh)) { - /* Yes, process the SFTP data. */ - ret = wolfSSH_SFTP_read(ssh); - error = wolfSSH_get_error(ssh); - timeout = (ret == WS_REKEYING) ? - TEST_SFTP_TIMEOUT : TEST_SFTP_TIMEOUT_NONE; - if (error == WS_WANT_READ || error == WS_WANT_WRITE || - error == WS_CHAN_RXD || error == WS_REKEYING || - error == WS_WINDOW_FULL) - ret = error; - if (error == WS_EOF) { - break; - } - continue; - } - ret = wolfSSH_stream_peek(ssh, peek_buf, sizeof(peek_buf)); if (ret > 0) { /* Yes, process the SFTP data. */ @@ -672,11 +766,14 @@ static int SFTP_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, } } - timeout = TEST_SFTP_TIMEOUT; } while (ret != WS_FATAL_ERROR); } (void)conn; +#ifdef _WIN32 + /* stop impersonating the user */ + RevertToSelf(); +#endif return ret; } #endif @@ -685,8 +782,368 @@ static int SFTP_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, #ifdef WOLFSSH_SHELL #ifndef MAX_COMMAND_SZ - #define MAX_COMMAND_SZ 80 +#define MAX_COMMAND_SZ 80 +#endif + +#ifdef WIN32 + +/* handles creating a new shell env. and maintains SSH connection for incoming + * user input as well as output of the shell. + * return WS_SUCCESS on success */ +static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, + WPASSWD* pPasswd, WOLFSSHD_CONFIG* usrConf, const char* subCmd) +{ + BOOL ret; + word32 shellChannelId = 0; +#ifndef EXAMPLE_BUFFER_SZ +#define EXAMPLE_BUFFER_SZ 4096 +#endif + byte shellBuffer[EXAMPLE_BUFFER_SZ]; + int cnt_r, cnt_w; + HANDLE ptyIn = NULL, ptyOut = NULL; + HANDLE cnslIn = NULL, cnslOut = NULL; + HPCON pCon = 0; + COORD cord; + STARTUPINFOEX ext; + PCWSTR sysCmd = L"c:\\windows\\system32\\cmd.exe"; +#if 0 + /* start powershell instead */ + PCWSTR sysCmd = L"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"; +#endif + PWSTR cmd = NULL; + size_t cmdSz = 0; + PROCESS_INFORMATION processInfo; + size_t sz = 0; + WCHAR h[MAX_PATH]; + char* forcedCmd; + + forcedCmd = wolfSSHD_ConfigGetForcedCmd(usrConf); + + /* @TODO check for conpty support LoadLibrary()and GetProcAddress(). */ + + + if (forcedCmd != NULL && WSTRCMP(forcedCmd, "internal-sftp") == 0) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Only SFTP connections allowed for user " + "%s", wolfSSH_GetUsername(ssh)); + return WS_FATAL_ERROR; + } + + ret = wolfSSHD_GetHomeDirectory(conn->auth, ssh, h, MAX_PATH); + if (ret == WS_SUCCESS) { + ZeroMemory(&ext, sizeof(STARTUPINFOEX)); + ZeroMemory(&processInfo, sizeof(PROCESS_INFORMATION)); + + /* use forced command if set over subCmd */ + if (forcedCmd == NULL && subCmd != NULL) { + forcedCmd = (char*)subCmd; + } + + if (forcedCmd != NULL) { /* copy over set command if present */ + /* +1 for terminator and +2 for quotes */ + cmdSz = WSTRLEN(forcedCmd) + wcslen(sysCmd) + WSTRLEN(" /C ") + 3; + cmd = (PWSTR)WMALLOC(sizeof(wchar_t) * cmdSz, NULL, DYNTYPE_SSHD); + if (cmd == NULL) { + ret = WS_MEMORY_E; + } + else { + WCHAR* tmp = (WCHAR*)WMALLOC(sizeof(wchar_t) * cmdSz, NULL, + DYNTYPE_SSHD); + if (tmp == NULL) { + ret = WS_MEMORY_E; + } + + if (ret == WS_SUCCESS) { + size_t wr = 0; + if (mbstowcs_s(&wr, tmp, cmdSz, forcedCmd, cmdSz - 1) != 0) { + ret = WS_FATAL_ERROR; + } + } + + if (ret == WS_SUCCESS) { + swprintf(cmd, cmdSz, L"%s /C \"%s\"", sysCmd, tmp); + } + + if (tmp != NULL) { + WFREE(tmp, NULL, DYNTYPE_SSHD); + } + + /* read in case a window-change packet might be queued */ + { + int rc; + word32 lastChannel = 0; + + do { + rc = wolfSSH_worker(ssh, &lastChannel); + if (rc < 0) { + rc = wolfSSH_get_error(ssh); + } + } while (rc == WS_WANT_WRITE); + } + } + } + else { /* when set command is not present start 'cmd.exe' */ + cmdSz = wcslen(sysCmd) + 1; /* +1 for terminator */ + cmd = (PWSTR)WMALLOC(sizeof(wchar_t) * cmdSz, NULL, DYNTYPE_SSHD); + if (cmd == NULL) { + ret = WS_MEMORY_E; + } + else { + wcscpy_s(cmd, cmdSz, sysCmd); + } + } + } + + if (ret == WS_SUCCESS) { + HRESULT err; + + CreatePipe(&cnslIn, &ptyIn, NULL, 0); + CreatePipe(&ptyOut, &cnslOut, NULL, 0); + + cord.X = ssh->curX; + cord.Y = ssh->curY; + + /* Sanity check on cord values, if 0 than assume was not set. + * (can happen with exec and not req-pty message) + * If not set yet then use sane default values. */ + if (cord.X == 0) { + cord.X = 80; + } + + if (cord.Y == 0) { + cord.Y = 24; + } + + err = CreatePseudoConsole(cord, cnslIn, cnslOut, 0, &pCon); + if (err != S_OK) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Issue creating pseudo console"); + ret = WS_FATAL_ERROR; + } + else { + CloseHandle(cnslIn); + CloseHandle(cnslOut); + wolfSSH_SetTerminalResizeCtx(ssh, (void*)&pCon); + } + } + + /* setup startup extended info for pseudo terminal */ + if (ret == WS_SUCCESS) { + ext.StartupInfo.cb = sizeof(STARTUPINFOEX); + (void)InitializeProcThreadAttributeList(NULL, 1, 0, &sz); + if (sz == 0) { + ret = WS_FATAL_ERROR; + } + + if (ret == WS_SUCCESS) { + /* Using HeapAlloc for better support when possibly passing + memory between Windows Modules */ + ext.lpAttributeList = + (PPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, sz); + if (ext.lpAttributeList == NULL) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Issue getting memory for attribute list"); + ret = WS_FATAL_ERROR; + } + } + + if (ret == WS_SUCCESS) { + if (InitializeProcThreadAttributeList(ext.lpAttributeList, 1, 0, + &sz) != TRUE) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Issue initializing proc thread attribute"); + ret = WS_FATAL_ERROR; + } + } + + if (ret == WS_SUCCESS) { + if (UpdateProcThreadAttribute(ext.lpAttributeList, 0, + PROC_THREAD_ATTRIBUTE_PSEUDOCONSOLE, + pCon, sizeof(HPCON), NULL, NULL) != TRUE) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Issue updating proc thread attribute"); + ret = WS_FATAL_ERROR; + } + } + } + + + if (ret == WS_SUCCESS) { +#if 1 + if (CreateProcessAsUserW(wolfSSHD_GetAuthToken(conn->auth), NULL, cmd, + NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT, NULL, h, + &ext.StartupInfo, &processInfo) != TRUE) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Issue creating process, Windows error %d", GetLastError()); + return WS_FATAL_ERROR; + } +#else + /* Needs enabled when running as non-service, compiled out for now to + * make sure it can not accidentally be used since the permissions of + * the created process match the current process. */ + if (CreateProcessW(NULL, cmd, NULL, NULL, FALSE, + EXTENDED_STARTUPINFO_PRESENT, NULL, h, &ext.StartupInfo, &processInfo) + != TRUE) { + wolfSSH_Log(WS_LOG_ERROR, + "[SSHD] Issue creating process, windows error %d", WSAGetLastError()); + if (cmd != NULL) { + WFREE(cmd, NULL, DYNTYPE_SSHD); + } + return WS_FATAL_ERROR; + } #endif + else { + SOCKET sshFd; + byte tmp[2]; + fd_set readFds; + WS_SOCKET_T maxFd; + int pending = 0; + int readPending = 0; + int rc = 0; + DWORD processState; + DWORD ava; + struct timeval t; + + t.tv_sec = 0; + t.tv_usec = 800; + + sshFd = wolfSSH_get_fd(ssh); + maxFd = sshFd; + + FD_ZERO(&readFds); + FD_SET(sshFd, &readFds); + + wolfSSH_Log(WS_LOG_INFO, "[SSHD] Successfully created process for " + "console, waiting for it to start"); + + WaitForInputIdle(processInfo.hProcess, 1000); + + do { + /* @TODO currently not blocking till data comes in */ + if (PeekNamedPipe(ptyOut, NULL, 0, NULL, &ava, NULL) == TRUE) { + if (ava > 0) { + readPending = 1; + } + } + + if (readPending == 0) { + /* check if process is still running before waiting to read */ + if (GetExitCodeProcess(processInfo.hProcess, &processState) + == TRUE) { + if (processState != STILL_ACTIVE) { + wolfSSH_Log(WS_LOG_INFO, + "[SSHD] Process has exited, exit state = %d, " + "close down SSH connection", processState); + Sleep(100); /* give the stdout/stderr of process a + * little time to write to pipe */ + if (PeekNamedPipe(ptyOut, NULL, 0, NULL, &ava, NULL) + == TRUE) { + if (ava > 0) { + /* if data still pending then continue + * sending it over SSH */ + readPending = 1; + continue; + } + } + break; + } + } + if (wolfSSH_stream_peek(ssh, tmp, 1) <= 0) { + rc = select((int)maxFd + 1, &readFds, NULL, NULL, &t); + if (rc == -1) { + wolfSSH_Log(WS_LOG_INFO, + "[SSHD] select call waiting on socket failed"); + break; + } + /* when select times out and no socket is set as ready + Windows overwrites readFds with 0. Reset the fd here + for next select call */ + if (rc == 0) { + FD_SET(sshFd, &readFds); + } + } + else { + pending = 1; + } + } + + if (rc != 0 && (pending || FD_ISSET(sshFd, &readFds))) { + word32 lastChannel = 0; + + /* The following tries to read from the first channel inside + the stream. If the pending data in the socket is for + another channel, this will return an error with id + WS_CHAN_RXD. That means the agent has pending data in its + channel. The additional channel is only used with the + agent. */ + cnt_r = wolfSSH_worker(ssh, &lastChannel); + if (cnt_r < 0) { + rc = wolfSSH_get_error(ssh); + if (rc == WS_CHAN_RXD) { + if (lastChannel == shellChannelId) { + cnt_r = wolfSSH_ChannelIdRead(ssh, + shellChannelId, shellBuffer, + sizeof shellBuffer); + if (cnt_r <= 0) + break; + pending = 0; + if (WriteFile(ptyIn, shellBuffer, cnt_r, &cnt_r, + NULL) != TRUE) { + wolfSSH_Log(WS_LOG_INFO, + "[SSHD] Error writing to pipe for " + "console"); + break; + } + } + } + else if (rc == WS_CHANNEL_CLOSED) { + continue; + } + else if (rc != WS_WANT_READ) { + break; + } + } + } + + if (readPending) { + WMEMSET(shellBuffer, 0, EXAMPLE_BUFFER_SZ); + + if (ReadFile(ptyOut, shellBuffer, EXAMPLE_BUFFER_SZ, &cnt_r, + NULL) != TRUE) { + wolfSSH_Log(WS_LOG_INFO, + "[SSHD] Error reading from pipe for console"); + break; + } + else { + readPending = 0; + if (cnt_r > 0) { + cnt_w = wolfSSH_ChannelIdSend(ssh, shellChannelId, + shellBuffer, cnt_r); + if (cnt_w < 0) + break; + } + } + } + } while (1); + + if (cmd != NULL) { + WFREE(cmd, NULL, DYNTYPE_SSHD); + } + wolfSSH_Log(WS_LOG_INFO, + "[SSHD] Closing down process for console"); + + if (ext.lpAttributeList != NULL) { + HeapFree(GetProcessHeap(), 0, ext.lpAttributeList); + } + + ClosePseudoConsole(pCon); + CloseHandle(processInfo.hThread); + CloseHandle(wolfSSHD_GetAuthToken(conn->auth)); + } + } + return ret; +} +#else /* handles creating a new shell env. and maintains SSH connection for incoming * user input as well as output of the shell. @@ -698,12 +1155,14 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, int rc; WS_SOCKET_T childFd = 0; pid_t childPid; + #ifndef EXAMPLE_BUFFER_SZ #define EXAMPLE_BUFFER_SZ 4096 #endif byte shellBuffer[EXAMPLE_BUFFER_SZ]; byte channelBuffer[EXAMPLE_BUFFER_SZ]; char* forcedCmd; + int windowFull = 0; forcedCmd = wolfSSHD_ConfigGetForcedCmd(usrConf); @@ -876,7 +1335,7 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, pending = 1; /* found some pending SSH data */ } - if (pending || FD_ISSET(sshFd, &readFds)) { + if (windowFull || pending || FD_ISSET(sshFd, &readFds)) { word32 lastChannel = 0; /* The following tries to read from the first channel inside @@ -910,6 +1369,19 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, } } + /* if the window was previously full, try resending the data */ + if (windowFull) { + cnt_w = wolfSSH_ChannelIdSend(ssh, shellChannelId, + shellBuffer, cnt_r); + if (cnt_w == WS_WINDOW_FULL) { + windowFull = 1; + continue; + } + else { + windowFull = 0; + } + } + if (FD_ISSET(childFd, &readFds)) { cnt_r = (int)read(childFd, shellBuffer, sizeof shellBuffer); /* This read will return 0 on EOF */ @@ -923,7 +1395,11 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, if (cnt_r > 0) { cnt_w = wolfSSH_ChannelIdSend(ssh, shellChannelId, shellBuffer, cnt_r); - if (cnt_w < 0) + if (cnt_w == WS_WINDOW_FULL) { + windowFull = 1; + continue; + } + else if (cnt_w < 0) break; } } @@ -934,8 +1410,13 @@ static int SHELL_Subsystem(WOLFSSHD_CONNECTION* conn, WOLFSSH* ssh, return WS_SUCCESS; } #endif +#endif +#ifdef WIN32 +static volatile int timeOut = 0; +#else static __thread int timeOut = 0; +#endif static void alarmCatch(int signum) { wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failed login within grace period"); @@ -950,14 +1431,21 @@ static int UserAuthResult(byte result, (void)userAuthResultCtx; if (result == WOLFSSH_USERAUTH_SUCCESS) { + #ifndef WIN32 + /* @TODO alarm catch on windows */ alarm(0); + #endif } return WS_SUCCESS; } /* handle wolfSSH accept and directing to correct subsystem */ +#ifdef _WIN32 +static DWORD HandleConnection(void* arg) +#else static void* HandleConnection(void* arg) +#endif { int ret = WS_SUCCESS; int error; @@ -989,8 +1477,12 @@ static void* HandleConnection(void* arg) /* set alarm for login grace time */ graceTime = wolfSSHD_AuthGetGraceTime(conn->auth); if (graceTime > 0) { + #ifdef WIN32 + /* @TODO SetTimer(NULL, NULL, graceTime, alarmCatch); */ + #else signal(SIGALRM, alarmCatch); alarm((unsigned int)graceTime); + #endif } ret = wolfSSH_accept(ssh); @@ -1036,6 +1528,7 @@ static void* HandleConnection(void* arg) ret = WS_FATAL_ERROR; } + #ifndef WIN32 if (ret == WS_SUCCESS || ret == WS_SFTP_COMPLETE || ret == WS_SCP_INIT) { pPasswd = getpwnam((const char *)usr); @@ -1044,6 +1537,7 @@ static void* HandleConnection(void* arg) ret = WS_FATAL_ERROR; } } + #endif if (ret != WS_FATAL_ERROR) { /* check for any forced command set for the user */ @@ -1093,6 +1587,7 @@ static void* HandleConnection(void* arg) case WOLFSSH_SESSION_UNKNOWN: case WOLFSSH_SESSION_EXEC: + #if defined(WOLFSSH_SHELL) if (ret == WS_SUCCESS) { wolfSSH_Log(WS_LOG_INFO, "[SSHD] Entering exec session [%s]", @@ -1100,6 +1595,7 @@ static void* HandleConnection(void* arg) SHELL_Subsystem(conn, ssh, pPasswd, usrConf, wolfSSH_GetSessionCommand(ssh)); } + #endif /* WOLFSH_SHELL */ /* SCP can be an exec type */ if (ret == WS_SCP_INIT) { @@ -1172,15 +1668,21 @@ static void* HandleConnection(void* arg) } wolfSSH_Log(WS_LOG_INFO, "[SSHD] Return from closing connection = %d", ret); +#ifdef _WIN32 + return 0; +#else return NULL; +#endif } /* returns WS_SUCCESS on success */ static int NewConnection(WOLFSSHD_CONNECTION* conn) { - int pd; + int ret = WS_SUCCESS; +#ifndef WIN32 + int pd = 0; pd = fork(); if (pd < 0) { @@ -1201,6 +1703,25 @@ static int NewConnection(WOLFSSHD_CONNECTION* conn) WCLOSESOCKET(conn->fd); } } +#else + HANDLE t; + DWORD id; + + if (conn->isThreaded) { + t = CreateThread(NULL, 0, HandleConnection, (void*)conn, 0, &id); + if (t == NULL) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue creating new thread"); + ret = WS_FATAL_ERROR; + } + else { + wolfSSH_Log(WS_LOG_INFO, "[SSHD] Spawned new thread %d\n", id); + CloseHandle(t); + } + } + else { + HandleConnection((void*)conn); + } +#endif return ret; } @@ -1223,7 +1744,7 @@ static int PendingConnection(WS_SOCKET_T fd) FD_SET(fd, &r); errno = 0; - ret = select(nfds, &r, &w, &e, &t); + ret = select((int)nfds, &r, &w, &e, &t); if (ret < 0) { /* a socket level issue happend, could just be a system call int. */ if (errno != EINTR) { @@ -1244,33 +1765,134 @@ static int PendingConnection(WS_SOCKET_T fd) return ret; } - int myoptind = 0; char* myoptarg = NULL; -int main(int argc, char** argv) +#ifdef _WIN32 +#include +#include + +SERVICE_STATUS serviceStatus = { 0 }; +SERVICE_STATUS_HANDLE serviceStatusHandle = NULL; +HANDLE serviceStop = INVALID_HANDLE_VALUE; + +#define WOLFSSHD_SERVICE_NAME _T("wolfSSHd") + + +static void wolfSSHD_ServiceCb(DWORD CtrlCode) +{ + switch (CtrlCode) { + case SERVICE_CONTROL_STOP: + if (serviceStatus.dwCurrentState != SERVICE_RUNNING) + break; + serviceStatus.dwControlsAccepted = 0; + serviceStatus.dwCurrentState = SERVICE_STOP_PENDING; + serviceStatus.dwWin32ExitCode = 0; + serviceStatus.dwCheckPoint = 4; + + if (SetServiceStatus(serviceStatusHandle, &serviceStatus) == FALSE) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue setting service status"); + } + + /* send out signal that the service is stopping */ + SetEvent(serviceStop); + break; + + default: + break; + } +} + + +static char* _convertHelper(WCHAR* in, void* heap) { + int retSz; + char* ret; + + retSz = (int)wcslen(in) * 2; + ret = (char*)WMALLOC(retSz + 1, heap, DYNTYPE_SSHD); + if (ret != NULL) { + size_t numConv = 0; + if (wcstombs_s(&numConv, ret, retSz, in, retSz) != 0) { + XFREE(ret, heap, DYNTYPE_SSHD); + ret = NULL; + } + } + return ret; +} + +static void StartSSHD(DWORD argc, LPTSTR* wargv) +#else +static int StartSSHD(int argc, char** argv) +#endif { - int ret = WS_SUCCESS; + int ret = WS_SUCCESS; word16 port = 0; WS_SOCKET_T listenFd = 0; int ch; WOLFSSHD_CONFIG* conf = NULL; - WOLFSSHD_AUTH* auth = NULL; + WOLFSSHD_AUTH* auth = NULL; WOLFSSH_CTX* ctx = NULL; byte isDaemon = 1; + byte testMode = 0; - const char* configFile = "/etc/ssh/sshd_config"; + const char* configFile = "/etc/ssh/sshd_config"; const char* hostKeyFile = NULL; - signal(SIGINT, interruptCatch); - + logFile = stderr; wolfSSH_SetLoggingCb(wolfSSHDLoggingCb); - wolfSSH_Debugging_ON(); #ifdef DEBUG_WOLFSSL wolfSSL_Debugging_ON(); #endif - logFile = stderr; +#ifdef _WIN32 + char** argv = NULL; + DWORD i; + LPWSTR* cmdArgs = NULL; + LPWSTR cmdLn; + int cmdArgC = 0; + + /* get what the command line was and parse arguments from it */ + cmdLn = GetCommandLineW(); + cmdArgs = CommandLineToArgvW(cmdLn, &cmdArgC); + if (cmdArgs == NULL) { + ret = WS_FATAL_ERROR; + } + argc = cmdArgC; + + if (ret == WS_SUCCESS) { + for (i = 0; i < argc; i++) { + if (WSTRCMP((char*)(cmdArgs[i]), "-D") == 0) { + isDaemon = 0; + } + } + } + + if (isDaemon) { + /* Set the logging to go to OutputDebugString */ + wolfSSH_SetLoggingCb(ServiceDebugCb); + + if (ret == WS_SUCCESS) { + /* we want the arguments to be normal char strings not wchar_t */ + argv = (char**)WMALLOC(argc * sizeof(char*), NULL, DYNTYPE_SSHD); + if (argv == NULL) { + ret = WS_MEMORY_E; + } + else { + unsigned int z; + for (z = 0; z < argc; z++) { + argv[z] = _convertHelper(cmdArgs[z], NULL); + } + } + } + } + else { + argv = (char**)wargv; + } +#endif + + signal(SIGINT, interruptCatch); + WSTARTTCP(); + if (ret == WS_SUCCESS) { wolfSSH_Init(); } @@ -1282,72 +1904,95 @@ int main(int argc, char** argv) } } - while ((ch = mygetopt(argc, argv, "?f:p:h:dDE:")) != -1) { + while ((ch = mygetopt(argc, argv, "?f:p:h:dDE:o:t")) != -1) { switch (ch) { - case 'f': - configFile = myoptarg; - break; + case 'f': + configFile = myoptarg; + break; - case 'p': - if (ret == WS_SUCCESS) { - if (myoptarg == NULL) { - ret = WS_BAD_ARGUMENT; - break; - } + case 'p': + if (ret == WS_SUCCESS) { + if (myoptarg == NULL) { + ret = WS_BAD_ARGUMENT; + break; + } - ret = XATOI(myoptarg); - if (ret < 0) { - fprintf(stderr, "Issue parsing port number %s\n", - myoptarg); - ret = WS_BAD_ARGUMENT; + ret = XATOI(myoptarg); + if (ret < 0) { + fprintf(stderr, "Issue parsing port number %s\n", + myoptarg); + ret = WS_BAD_ARGUMENT; + } + else { + if (ret <= (word16)-1) { + port = (word16)ret; + ret = WS_SUCCESS; } else { - if (ret <= (word16)-1) { - port = (word16)ret; - ret = WS_SUCCESS; - } - else { - fprintf(stderr, "Port number %d too big.\n", ret); - ret = WS_BAD_ARGUMENT; - } + fprintf(stderr, "Port number %d too big.\n", ret); + ret = WS_BAD_ARGUMENT; } } - break; + } + break; - case 'h': - hostKeyFile = myoptarg; - break; + case 'h': + hostKeyFile = myoptarg; + break; - case 'd': - debugMode = 1; /* turn on debug mode */ - break; + case 'd': + debugMode = 1; /* turn on debug mode */ + break; - case 'D': - isDaemon = 0; - break; + case 'D': + isDaemon = 0; + break; - case 'E': - logFile = fopen(myoptarg, "ab"); - if (logFile == NULL) { - fprintf(stderr, "Unable to open log file %s\n", myoptarg); - ret = WS_FATAL_ERROR; - } - break; + case 'E': + ret = WFOPEN(NULL, &logFile, myoptarg, "ab"); + if (ret != 0 || logFile == WBADFILE) { + fprintf(stderr, "Unable to open log file %s\n", myoptarg); + ret = WS_FATAL_ERROR; + } + break; - case '?': - ShowUsage(); - return WS_SUCCESS; + case 'o': + #ifdef WOLFSSH_IGNORE_UNKNOWN_CONFIG + wolfSSH_Log(WS_LOG_DEBUG, "[SSHD] ignoring -o."); + break; + #else + ShowUsage(); + return WS_FATAL_ERROR; + #endif - default: - ShowUsage(); - return WS_SUCCESS; + case 't': + testMode = 1; + break; + + case '?': + ShowUsage(); + #ifndef _WIN32 + return WS_SUCCESS; + #else + return; + #endif + + default: + ShowUsage(); + #ifndef _WIN32 + return WS_SUCCESS; + #else + return; + #endif } } if (ret == WS_SUCCESS) { ret = wolfSSHD_ConfigLoad(conf, configFile); - if (ret != WS_SUCCESS) - fprintf(stderr, "Error reading in configure file %s\n", configFile); + if (ret != WS_SUCCESS) { + wolfSSH_Log(WS_LOG_ERROR, "Error reading in configure file %s\n", + configFile); + } } /* port was not overridden with argument, read from config file */ @@ -1364,9 +2009,6 @@ int main(int argc, char** argv) wolfSSH_Log(WS_LOG_INFO, "[SSHD] Starting wolfSSH SSHD application"); ret = SetupCTX(conf, &ctx); } - else { - /* TODO: handle error. */ - } if (ret == WS_SUCCESS) { auth = wolfSSHD_AuthCreateUser(NULL, conf); @@ -1380,7 +2022,8 @@ int main(int argc, char** argv) logFile = stderr; } - /* run as a daemon */ + /* run as a daemon or service */ +#ifndef WIN32 if (ret == WS_SUCCESS && isDaemon) { pid_t p; @@ -1426,8 +2069,8 @@ int main(int argc, char** argv) } else { if (dup2(fd, STDIN_FILENO) < 0 || - dup2(fd, STDOUT_FILENO) < 0 || - dup2(fd, STDERR_FILENO) < 0) { + dup2(fd, STDOUT_FILENO) < 0 || + dup2(fd, STDERR_FILENO) < 0) { ret = WS_FATAL_ERROR; } close(fd); @@ -1435,74 +2078,199 @@ int main(int argc, char** argv) } } } - - if (ret == WS_SUCCESS) { - if (wolfSSHD_AuthReducePermissions(auth) != WS_SUCCESS) { - wolfSSH_Log(WS_LOG_INFO, "[SSHD] Error lowering permissions level"); +#else + if (isDaemon) { + /* Set function to handle service query and commands */ + serviceStatusHandle = RegisterServiceCtrlHandler(WOLFSSHD_SERVICE_NAME, wolfSSHD_ServiceCb); + if (serviceStatusHandle == NULL) { ret = WS_FATAL_ERROR; } + else { + /* Update service status as 'start pending' */ + ZeroMemory(&serviceStatus, sizeof(serviceStatus)); + serviceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS; + serviceStatus.dwCurrentState = SERVICE_START_PENDING; + if (SetServiceStatus(serviceStatusHandle, &serviceStatus) == FALSE) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue updating service status"); + } + } + + /* Create a stop event to watch on */ + serviceStop = CreateEvent(NULL, TRUE, FALSE, NULL); + if (serviceStop == NULL) { + serviceStatus.dwControlsAccepted = 0; + serviceStatus.dwCurrentState = SERVICE_STOPPED; + serviceStatus.dwWin32ExitCode = GetLastError(); + serviceStatus.dwCheckPoint = 1; + + if (SetServiceStatus(serviceStatusHandle, &serviceStatus) == FALSE) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue updating service status"); + } + return; + } + + if (cmdArgs != NULL) { + LocalFree(cmdArgs); + } } +#endif - if (ret == WS_SUCCESS) { + if (ret == WS_SUCCESS && !testMode) { + wolfSSHD_ConfigSavePID(conf); wolfSSH_Log(WS_LOG_INFO, "[SSHD] Starting to listen on port %d", port); tcp_listen(&listenFd, &port, 1); wolfSSH_Log(WS_LOG_INFO, "[SSHD] Listening on port %d", port); + if (wolfSSHD_AuthReducePermissions(auth) != WS_SUCCESS) { + wolfSSH_Log(WS_LOG_INFO, "[SSHD] Error lowering permissions level"); + ret = WS_FATAL_ERROR; + } + #ifdef WIN32 + if (ret == WS_SUCCESS && isDaemon) { + /* update service status as started */ + serviceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP; + serviceStatus.dwCurrentState = SERVICE_RUNNING; + serviceStatus.dwWin32ExitCode = 0; + serviceStatus.dwCheckPoint = 0; + + if (SetServiceStatus(serviceStatusHandle, &serviceStatus) == FALSE) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue updating service status"); + } + } + #endif /* wait for incoming connections and fork them off */ while (ret == WS_SUCCESS && quit == 0) { WOLFSSHD_CONNECTION conn; - #ifdef WOLFSSL_NUCLEUS +#ifdef WOLFSSL_NUCLEUS struct addr_struct clientAddr; - #else +#else SOCKADDR_IN_T clientAddr; socklen_t clientAddrSz = sizeof(clientAddr); - #endif +#endif conn.auth = auth; - conn.listenFd = listenFd; + conn.listenFd = (int)listenFd; + conn.isThreaded = isDaemon; /* wait for a connection */ if (PendingConnection(listenFd)) { conn.ctx = ctx; - #ifdef WOLFSSL_NUCLEUS +#ifdef WOLFSSL_NUCLEUS conn.fd = NU_Accept(listenFd, &clientAddr, 0); - #else - conn.fd = accept(listenFd, (struct sockaddr*)&clientAddr, - &clientAddrSz); +#else + conn.fd = (int)accept(listenFd, (struct sockaddr*)&clientAddr, + &clientAddrSz); if (conn.fd >= 0) { inet_ntop(AF_INET, &clientAddr.sin_addr, conn.ip, INET_ADDRSTRLEN); } - #endif +#endif { - #ifdef USE_WINDOWS_API - unsigned long blocking = 1; - int ret = ioctlsocket(conn.fd, FIONBIO, &blocking); - if (ret == SOCKET_ERROR) - err_sys("ioctlsocket failed"); - #elif defined(WOLFSSL_MDK_ARM) || defined(WOLFSSL_KEIL_TCP_NET) \ +#ifdef USE_WINDOWS_API + unsigned long blocking = 1; + if (ioctlsocket(conn.fd, FIONBIO, &blocking) + == SOCKET_ERROR) + err_sys("ioctlsocket failed"); +#elif defined(WOLFSSL_MDK_ARM) || defined(WOLFSSL_KEIL_TCP_NET) \ || defined (WOLFSSL_TIRTOS)|| defined(WOLFSSL_VXWORKS) || \ defined(WOLFSSL_NUCLEUS) - /* non blocking not supported, for now */ - #else - int flags = fcntl(conn.fd, F_GETFL, 0); - if (flags < 0) - err_sys("fcntl get failed"); - flags = fcntl(conn.fd, F_SETFL, flags | O_NONBLOCK); - if (flags < 0) - err_sys("fcntl set failed"); - #endif + /* non blocking not supported, for now */ +#else + int flags = fcntl(conn.fd, F_GETFL, 0); + if (flags < 0) + err_sys("fcntl get failed"); + flags = fcntl(conn.fd, F_SETFL, flags | O_NONBLOCK); + if (flags < 0) + err_sys("fcntl set failed"); +#endif } ret = NewConnection(&conn); } +#ifdef _WIN32 + /* check if service has been shutdown */ + if (isDaemon && WaitForSingleObject(serviceStop, 0) == WAIT_OBJECT_0) { + quit = 1; + } +#endif } } +#ifdef _WIN32 + /* close down windows service */ + if (isDaemon) { + CloseHandle(serviceStop); + + serviceStatus.dwControlsAccepted = 0; + serviceStatus.dwCurrentState = SERVICE_STOPPED; + serviceStatus.dwWin32ExitCode = 0; + serviceStatus.dwCheckPoint = 3; + + if (serviceStatusHandle != NULL && + SetServiceStatus(serviceStatusHandle, &serviceStatus) == FALSE) { + wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Issue updating service status"); + } + } +#endif + CleanupCTX(conf, &ctx); wolfSSHD_ConfigFree(conf); wolfSSHD_AuthFreeUser(auth); wolfSSH_Cleanup(); +#ifdef _WIN32 + if (isDaemon) { /* free up temporary memory used for conversion of args from wchar_t */ + unsigned int z; + for (z = 0; z < argc; z++) { + WFREE(argv[z], NULL, DYNTYPE_SSHD); + } + WFREE(argv, NULL, DYNTYPE_SSHD); + } +#else + return 0; +#endif +} + +int main(int argc, char** argv) +{ +#ifdef _WIN32 + /* First look if this is a service being started */ + int i, isService = 1; + for (i = 0; i < argc; i++) { + if (WSTRCMP(argv[i], "-D") == 0) { + isService = 0; + } + } + + if (isService) { + SERVICE_TABLE_ENTRY ServiceTable[] = + { + {_T("wolfSSHd"), (LPSERVICE_MAIN_FUNCTION)StartSSHD}, + {NULL, NULL} + }; + + if (StartServiceCtrlDispatcher(ServiceTable) == FALSE) { + printf("StartServiceCtrlDispatcher failed\n"); + return GetLastError(); + } + } + else { + StartSSHD(argc, (LPSTR*)argv); + } return 0; +#else + return StartSSHD(argc, argv); +#endif +} + +#else + +#include + +/* helpful print out if compiling without SSHD feature enabled */ +int main(int argc, char** argv) +{ + printf("Not compiled in. Please recompile wolfSSH with :\n"); + printf("--enable-sshd (user_settings.h macro define WOLFSSH_SSHD\n"); + return -1; } #endif /* WOLFSSH_SSHD */ diff --git a/autogen.sh b/autogen.sh index b324bfef8..e1b5bf796 100755 --- a/autogen.sh +++ b/autogen.sh @@ -1,44 +1,20 @@ #!/bin/sh # # Create configure and makefile stuff... -# - -set -e - -# if get an error about libtool not setup -# " error: Libtool library used but 'LIBTOOL' is undefined -# The usual way to define 'LIBTOOL' is to add 'LT_INIT' " -# manually call libtoolize or glibtoolize before running this again -# (g)libtoolize - -# if you get an error about config.rpath missing, some buggy automake versions -# then touch the missing file (may need to make config/ first). -# touch config/config.rpath -# touch config.rpath - -if test ! -d build-aux; then - echo "Making missing build-aux directory." - mkdir -p build-aux -fi - -if test ! -f build-aux/config.rpath; then - echo "Touching missing build-aux/config.rpath file." - touch build-aux/config.rpath -fi # Git hooks should come before autoreconf. -if test -d .git; then - if ! test -d .git/hooks; then - mkdir .git/hooks - fi - ln -s -f ../../scripts/pre-commit.sh .git/hooks/pre-commit +if test -d .git +then + mkdir -p .git/hooks && ln -sf ../../scripts/pre-commit.sh .git/hooks/pre-commit fi # If this is a source checkout then call autoreconf with error as well -if test -e .git; then +if test -e .git +then WARNINGS="all,error" else WARNINGS="all" fi +export WARNINGS -autoreconf --install --force --verbose +autoreconf -ivf diff --git a/examples/client/client.c b/examples/client/client.c index 045fb4ff1..d38d316da 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -148,10 +148,17 @@ static int NonBlockSSH_connect(WOLFSSH* ssh) printf("... client would write block\n"); select_ret = tcp_select(sockfd, 1); - if (select_ret == WS_SELECT_RECV_READY || + + /* Continue in want write cases even if did not select on socket + * because there could be pending data to be written. Added continue + * on want write for test cases where a forced want read was introduced + * and the socket will not be receiving more data. */ + if (error == WS_WANT_WRITE || error == WS_WANT_READ || + select_ret == WS_SELECT_RECV_READY || select_ret == WS_SELECT_ERROR_READY) { ret = wolfSSH_connect(ssh); + error = wolfSSH_get_error(ssh); } else if (select_ret == WS_SELECT_TIMEOUT) error = WS_WANT_READ; @@ -168,6 +175,7 @@ typedef struct thread_args { WOLFSSH* ssh; wolfSSL_Mutex lock; byte rawMode; + byte quit; } thread_args; #ifdef _POSIX_THREADS @@ -181,6 +189,112 @@ typedef struct thread_args { #define THREAD_RET_SUCCESS 0 #endif + +static int sendCurrentWindowSize(thread_args* args) +{ + int ret; + word32 col = 80, row = 24, xpix = 0, ypix = 0; + + wc_LockMutex(&args->lock); +#if defined(_MSC_VER) + { + CONSOLE_SCREEN_BUFFER_INFO cs; + + if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cs) != 0) { + col = cs.srWindow.Right - cs.srWindow.Left + 1; + row = cs.srWindow.Bottom - cs.srWindow.Top + 1; + } + } +#else + { + struct winsize windowSize = { 0,0,0,0 }; + + ioctl(STDOUT_FILENO, TIOCGWINSZ, &windowSize); + col = windowSize.ws_col; + row = windowSize.ws_row; + xpix = windowSize.ws_xpixel; + ypix = windowSize.ws_ypixel; + } +#endif + ret = wolfSSH_ChangeTerminalSize(args->ssh, col, row, xpix, ypix); + wc_UnLockMutex(&args->lock); + + return ret; +} + + +#ifndef _MSC_VER + +#if (defined(__OSX__) || defined(__APPLE__)) +#include +dispatch_semaphore_t windowSem; +#else +#include +static sem_t windowSem; +#endif + +/* capture window change signales */ +static void WindowChangeSignal(int sig) +{ +#if (defined(__OSX__) || defined(__APPLE__)) + dispatch_semaphore_signal(windowSem); +#else + sem_post(&windowSem); +#endif + (void)sig; +} + +/* thread for handling window size adjustments */ +static THREAD_RET windowMonitor(void* in) +{ + thread_args* args; + int ret; + + args = (thread_args*)in; + do { + #if (defined(__OSX__) || defined(__APPLE__)) + dispatch_semaphore_wait(windowSem, DISPATCH_TIME_FOREVER); + #else + sem_wait(&windowSem); + #endif + if (args->quit) { + break; + } + ret = sendCurrentWindowSize(args); + (void)ret; + } while (1); + + return THREAD_RET_SUCCESS; +} +#else +/* no SIGWINCH on Windows, poll current terminal size */ +static word32 prevCol, prevRow; + +static int windowMonitor(thread_args* args) +{ + word32 row, col; + int ret = WS_SUCCESS; + CONSOLE_SCREEN_BUFFER_INFO cs; + + if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cs) != 0) { + col = cs.srWindow.Right - cs.srWindow.Left + 1; + row = cs.srWindow.Bottom - cs.srWindow.Top + 1; + + if (prevCol != col || prevRow != row) { + prevCol = col; + prevRow = row; + + wc_LockMutex(&args->lock); + ret = wolfSSH_ChangeTerminalSize(args->ssh, col, row, 0, 0); + wc_UnLockMutex(&args->lock); + } + } + + return ret; +} +#endif + + static THREAD_RET readInput(void* in) { byte buf[256]; @@ -198,6 +312,7 @@ static THREAD_RET readInput(void* in) /* Using A version to avoid potential 2 byte chars */ ret = ReadConsoleA(stdinHandle, (void*)buf, bufSz - 1, (DWORD*)&sz, NULL); + (void)windowMonitor(args); #else ret = (int)read(STDIN_FILENO, buf, bufSz -1); sz = (word32)ret; @@ -233,11 +348,11 @@ static THREAD_RET readInput(void* in) static THREAD_RET readPeer(void* in) { - byte buf[80]; + byte buf[256]; int bufSz = sizeof(buf); thread_args* args = (thread_args*)in; int ret = 0; - int fd = wolfSSH_get_fd(args->ssh); + int fd = (int)wolfSSH_get_fd(args->ssh); word32 bytes; #ifdef USE_WINDOWS_API HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE); @@ -250,7 +365,18 @@ static THREAD_RET readPeer(void* in) FD_SET(fd, &readSet); FD_SET(fd, &errSet); +#ifdef USE_WINDOWS_API + /* set handle to use for window resize */ + wc_LockMutex(&args->lock); + wolfSSH_SetTerminalResizeCtx(args->ssh, stdoutHandle); + wc_UnLockMutex(&args->lock); +#endif + while (ret >= 0) { + #ifdef USE_WINDOWS_API + (void)windowMonitor(args); + #endif + bytes = select(fd + 1, &readSet, NULL, &errSet, NULL); wc_LockMutex(&args->lock); while (bytes > 0 && (FD_ISSET(fd, &readSet) || FD_ISSET(fd, &errSet))) { @@ -264,14 +390,24 @@ static THREAD_RET readPeer(void* in) if (ret < 0) err_sys("Extended data read failed."); buf[bufSz - 1] = '\0'; + #ifdef USE_WINDOWS_API fprintf(stderr, "%s", buf); + #else + if (write(STDERR_FILENO, buf, ret) < 0) { + perror("Issue with stderr write "); + } + #endif } while (ret > 0); } else if (ret <= 0) { - #ifdef WOLFSSH_AGENT if (ret == WS_FATAL_ERROR) { ret = wolfSSH_get_error(args->ssh); - if (ret == WS_CHAN_RXD) { + if (ret == WS_WANT_READ) { + /* If WANT_READ, not an error. */ + ret = WS_SUCCESS; + } + #ifdef WOLFSSH_AGENT + else if (ret == WS_CHAN_RXD) { byte agentBuf[512]; int rxd, txd; word32 channel = 0; @@ -302,9 +438,9 @@ static THREAD_RET readPeer(void* in) WMEMSET(agentBuf, 0, sizeof(agentBuf)); continue; } + #endif /* WOLFSSH_AGENT */ } - #endif - if (ret != WS_EOF) { + else if (ret != WS_EOF) { err_sys("Stream read failed."); } } @@ -327,7 +463,9 @@ static THREAD_RET readPeer(void* in) fflush(stdout); } #else - printf("%s", buf); + if (write(STDOUT_FILENO, buf, ret) < 0) { + perror("write to stdout error "); + } fflush(stdout); #endif } @@ -629,7 +767,7 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) } else #endif - { + if (pubKeyName) { ret = ClientUsePubKey(pubKeyName, userEcc); } if (ret != 0) { @@ -728,14 +866,47 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) if (cmd != NULL || keepOpen == 1) { #if defined(_POSIX_THREADS) thread_args arg; - pthread_t thread[2]; + pthread_t thread[3]; arg.ssh = ssh; + arg.quit = 0; wc_InitMutex(&arg.lock); - pthread_create(&thread[0], NULL, readInput, (void*)&arg); - pthread_create(&thread[1], NULL, readPeer, (void*)&arg); - pthread_join(thread[1], NULL); - pthread_cancel(thread[0]); + #if (defined(__OSX__) || defined(__APPLE__)) + windowSem = dispatch_semaphore_create(0); + #else + sem_init(&windowSem, 0, 0); + #endif + + if (cmd) { + int err; + + /* exec command does not contain initial terminal size, unlike pty-req. + * Send an inital terminal size for recieving the results of the command */ + err = sendCurrentWindowSize(&arg); + if (err != WS_SUCCESS) { + fprintf(stderr, "Issue sending exec initial terminal size\n\r"); + } + } + + signal(SIGWINCH, WindowChangeSignal); + pthread_create(&thread[0], NULL, windowMonitor, (void*)&arg); + pthread_create(&thread[1], NULL, readInput, (void*)&arg); + pthread_create(&thread[2], NULL, readPeer, (void*)&arg); + pthread_join(thread[2], NULL); + /* Wake the windowMonitor thread so it can exit. */ + arg.quit = 1; + #if (defined(__OSX__) || defined(__APPLE__)) + dispatch_semaphore_signal(windowSem); + #else + sem_post(&windowSem); + #endif + pthread_join(thread[0], NULL); + pthread_cancel(thread[1]); + #if (defined(__OSX__) || defined(__APPLE__)) + dispatch_release(windowSem); + #else + sem_destroy(&windowSem); + #endif #elif defined(_MSC_VER) thread_args arg; HANDLE thread[2]; @@ -743,6 +914,18 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) arg.ssh = ssh; arg.rawMode = rawMode; wc_InitMutex(&arg.lock); + + if (cmd) { + int err; + + /* exec command does not contain initial terminal size, unlike pty-req. + * Send an inital terminal size for recieving the results of the command */ + err = sendCurrentWindowSize(&arg); + if (err != WS_SUCCESS) { + fprintf(stderr, "Issue sending exec initial terminal size\n\r"); + } + } + thread[0] = CreateThread(NULL, 0, readInput, (void*)&arg, 0, 0); thread[1] = CreateThread(NULL, 0, readPeer, (void*)&arg, 0, 0); WaitForSingleObject(thread[1], INFINITE); @@ -785,18 +968,21 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) #endif } ret = wolfSSH_shutdown(ssh); - if (ret != WS_SUCCESS) { - err_sys("Sending the shutdown messages failed."); - } - ret = wolfSSH_worker(ssh, NULL); - if (ret != WS_SUCCESS) { - err_sys("Failed to listen for close messages from the peer."); + /* do not continue on with shutdown process if peer already disconnected */ + if (ret != WS_SOCKET_ERROR_E && wolfSSH_get_error(ssh) != WS_SOCKET_ERROR_E) { + if (ret != WS_SUCCESS) { + err_sys("Sending the shutdown messages failed."); + } + ret = wolfSSH_worker(ssh, NULL); + if (ret != WS_SUCCESS) { + err_sys("Failed to listen for close messages from the peer."); + } } WCLOSESOCKET(sockFd); wolfSSH_free(ssh); wolfSSH_CTX_free(ctx); - if (ret != WS_SUCCESS) - err_sys("Closing client stream failed. Connection could have been closed by peer"); + if (ret != WS_SUCCESS && ret != WS_SOCKET_ERROR_E) + err_sys("Closing client stream failed"); ClientFreeBuffers(pubKeyName, privKeyName); #if !defined(WOLFSSH_NO_ECC) && defined(FP_ECC) && defined(HAVE_THREAD_LS) diff --git a/examples/client/common.c b/examples/client/common.c index d66603ca3..9578410b2 100644 --- a/examples/client/common.c +++ b/examples/client/common.c @@ -248,29 +248,29 @@ static int load_der_file(const char* filename, byte** out, word32* outSz) if (filename == NULL || out == NULL || outSz == NULL) return -1; - ret = WFOPEN(&file, filename, "rb"); + ret = WFOPEN(NULL, &file, filename, "rb"); if (ret != 0 || file == WBADFILE) return -1; - if (WFSEEK(file, 0, WSEEK_END) != 0) { - WFCLOSE(file); + if (WFSEEK(NULL, file, 0, WSEEK_END) != 0) { + WFCLOSE(NULL, file); return -1; } - inSz = (word32)WFTELL(file); - WREWIND(file); + inSz = (word32)WFTELL(NULL, file); + WREWIND(NULL, file); if (inSz == 0) { - WFCLOSE(file); + WFCLOSE(NULL, file); return -1; } in = (byte*)WMALLOC(inSz, NULL, 0); if (in == NULL) { - WFCLOSE(file); + WFCLOSE(NULL, file); return -1; } - ret = (int)WFREAD(in, 1, inSz, file); + ret = (int)WFREAD(NULL, in, 1, inSz, file); if (ret <= 0 || (word32)ret != inSz) { ret = -1; WFREE(in, NULL, 0); @@ -283,7 +283,7 @@ static int load_der_file(const char* filename, byte** out, word32* outSz) *out = in; *outSz = inSz; - WFCLOSE(file); + WFCLOSE(NULL, file); return ret; } @@ -382,25 +382,26 @@ int ClientPublicKeyCheck(const byte* pubKey, word32 pubKeySz, void* ctx) if (ParseRFC6187(pubKey, pubKeySz, &der, &derSz) == WS_SUCCESS) { wc_InitDecodedCert(&dCert, der, derSz, NULL); if (wc_ParseCert(&dCert, CERT_TYPE, NO_VERIFY, NULL) != 0) { - printf("public key not a cert\n"); + WLOG(WS_LOG_DEBUG, "public key not a cert"); } else { int ipMatch = 0; DNS_entry* current = dCert.altNames; if (ctx == NULL) { - fprintf(stderr, "No host IP set to check against!\n"); + WLOG(WS_LOG_ERROR, "No host IP set to check against!"); ret = -1; } if (ret == 0) { while (current != NULL) { if (current->type == ASN_IP_TYPE) { - printf("host cert alt. name IP : %s\n", + WLOG(WS_LOG_DEBUG, "host cert alt. name IP : %s", current->ipString); - printf("\texpecting host IP : %s\n", (char*)ctx); + WLOG(WS_LOG_DEBUG, + "\texpecting host IP : %s", (char*)ctx); if (XSTRCMP(ctx, current->ipString) == 0) { - printf("\tmatched!\n"); + WLOG(WS_LOG_DEBUG, "\tmatched!"); ipMatch = 1; } } @@ -424,8 +425,8 @@ int ClientPublicKeyCheck(const byte* pubKey, word32 pubKeySz, void* ctx) } } #else - printf("wolfSSL not built with OPENSSL_ALL or WOLFSSL_IP_ALT_NAME\n"); - printf("\tnot checking IP address from peer's cert\n"); + WLOG(WS_LOG_DEBUG, "wolfSSL not built with OPENSSL_ALL or WOLFSSL_IP_ALT_NAME"); + WLOG(WS_LOG_DEBUG, "\tnot checking IP address from peer's cert"); #endif #endif diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index ebff84394..ab1338e0d 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -1411,7 +1411,7 @@ static int load_file(const char* fileName, byte* buf, word32* bufSz) if (fileName == NULL) return 0; - if (WFOPEN(&file, fileName, "rb") != 0) + if (WFOPEN(NULL, &file, fileName, "rb") != 0) return 0; fseek(file, 0, XSEEK_END); fileSz = (word32)ftell(file); @@ -2511,10 +2511,10 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) #ifndef NO_FILESYSTEM WFILE* f = NULL; int ret; - ret = WFOPEN(&f, readyFile, "w"); + ret = WFOPEN(NULL, &f, readyFile, "w"); if (f != NULL && ret == 0) { fprintf(f, "%d\n", (int)port); - WFCLOSE(f); + WFCLOSE(NULL, f); } #endif } diff --git a/examples/scpclient/scpclient.c b/examples/scpclient/scpclient.c index b28210639..3ee5a5334 100644 --- a/examples/scpclient/scpclient.c +++ b/examples/scpclient/scpclient.c @@ -310,17 +310,20 @@ THREAD_RETURN WOLFSSH_THREAD scp_client(void* args) err_sys("Couldn't copy the file."); ret = wolfSSH_shutdown(ssh); - if (ret != WS_SUCCESS) { - err_sys("Sending the shutdown messages failed."); - } - ret = wolfSSH_worker(ssh, NULL); - if (ret != WS_SUCCESS && ret != WS_CHANNEL_CLOSED) { - err_sys("Failed to listen for close messages from the peer."); + /* do not continue on with shutdown process if peer already disconnected */ + if (ret != WS_SOCKET_ERROR_E && wolfSSH_get_error(ssh) != WS_SOCKET_ERROR_E) { + if (ret != WS_SUCCESS) { + err_sys("Sending the shutdown messages failed."); + } + ret = wolfSSH_worker(ssh, NULL); + if (ret != WS_SUCCESS && ret != WS_CHANNEL_CLOSED) { + err_sys("Failed to listen for close messages from the peer."); + } } WCLOSESOCKET(sockFd); wolfSSH_free(ssh); wolfSSH_CTX_free(ctx); - if (ret != WS_SUCCESS) + if (ret != WS_SUCCESS && ret != WS_SOCKET_ERROR_E) err_sys("Closing scp stream failed. Connection could have been closed by peer"); ClientFreeBuffers(pubKeyName, privKeyName); diff --git a/examples/server/server.c b/examples/server/server.c index abdcb891f..1136d0135 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -282,7 +282,7 @@ static int load_file(const char* fileName, byte* buf, word32 bufSz) if (fileName == NULL) return 0; - if (WFOPEN(&file, fileName, "rb") != 0) + if (WFOPEN(NULL, &file, fileName, "rb") != 0) return 0; fseek(file, 0, SEEK_END); fileSz = (word32)ftell(file); diff --git a/examples/sftpclient/sftpclient.c b/examples/sftpclient/sftpclient.c index 5f084adcf..52b6b89f8 100644 --- a/examples/sftpclient/sftpclient.c +++ b/examples/sftpclient/sftpclient.c @@ -24,6 +24,12 @@ #define WOLFSSH_TEST_CLIENT +#ifdef WOLFSSL_USER_SETTINGS +#include +#else +#include +#endif + #include #include #include @@ -1032,8 +1038,9 @@ static int doAutopilot(int cmd, char* local, char* remote) } if (remoteAbsPath) { - WMEMSET(fullpath, 0, sizeof(fullpath)); - WSTRNCPY(fullpath, remote, sizeof(fullpath) - 1); + /* use remote absolute path if provided */ + WMEMSET(fullpath, 0, sizeof(fullpath)); + WSTRNCPY(fullpath, remote, sizeof(fullpath) - 1); } else { do { @@ -1244,7 +1251,7 @@ THREAD_RETURN WOLFSSH_THREAD sftpclient_test(void* args) else wolfSSH_SetUserAuth(ctx, ((func_args*)args)->user_auth); -#ifndef WS_NO_SIGNAL +#if !defined(WS_NO_SIGNAL) && !defined(USE_WINDOWS_API) /* handle interrupt with get and put */ signal(SIGINT, sig_handler); #endif @@ -1332,7 +1339,11 @@ THREAD_RETURN WOLFSSH_THREAD sftpclient_test(void* args) WFREE(workingDir, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (ret == WS_SUCCESS) { if (wolfSSH_shutdown(ssh) != WS_SUCCESS) { - printf("error with wolfSSH_shutdown(), already disconnected?\n"); + int rc; + rc = wolfSSH_get_error(ssh); + + if (rc != WS_SOCKET_ERROR_E && rc != WS_EOF) + printf("error with wolfSSH_shutdown()\n"); } } WCLOSESOCKET(sockFd); diff --git a/ide/winvs/README.md b/ide/winvs/README.md index 3edc9c49a..9e6054b12 100644 --- a/ide/winvs/README.md +++ b/ide/winvs/README.md @@ -55,3 +55,19 @@ This value is used in the debugging environment for the echoserver's When you run the echoserver from the debugger, it finds the wolfSSL DLL in that directory. + + +SSHD Service +----------- + +Creating a new service +`sc.exe create wolfSSHd binpath="D:\work\wolfssh\ide\winvs\Debug\x64\wolfsshd.exe -f -h -p "` + +Starting wolfSSHd service run the following command in an adminstrator power shell session: +`sc.exe start wolfSSHd` + +To stop the service run the following in an adminstrator power shell session: +`sc.exe stop wolfSSHd` + +To delete the service run +`sc.exe delete wolfSSHd` diff --git a/ide/winvs/api-test/api-test.vcxproj.user b/ide/winvs/api-test/api-test.vcxproj.user deleted file mode 100644 index 23a5dce7e..000000000 --- a/ide/winvs/api-test/api-test.vcxproj.user +++ /dev/null @@ -1,19 +0,0 @@ - - - - WindowsLocalDebugger - PATH=$(wolfCryptDllDebug32);%PATH% - - - WindowsLocalDebugger - PATH=$(wolfCryptDllDebug64);%PATH% - - - WindowsLocalDebugger - PATH=$(wolfCryptDllRelease32);%PATH% - - - WindowsLocalDebugger - PATH=$(wolfCryptDllRelease64);%PATH% - - diff --git a/ide/winvs/client/client.vcxproj.user b/ide/winvs/client/client.vcxproj.user deleted file mode 100644 index 23a5dce7e..000000000 --- a/ide/winvs/client/client.vcxproj.user +++ /dev/null @@ -1,19 +0,0 @@ - - - - WindowsLocalDebugger - PATH=$(wolfCryptDllDebug32);%PATH% - - - WindowsLocalDebugger - PATH=$(wolfCryptDllDebug64);%PATH% - - - WindowsLocalDebugger - PATH=$(wolfCryptDllRelease32);%PATH% - - - WindowsLocalDebugger - PATH=$(wolfCryptDllRelease64);%PATH% - - diff --git a/ide/winvs/echoserver/echoserver.vcxproj.user b/ide/winvs/echoserver/echoserver.vcxproj.user deleted file mode 100644 index 23a5dce7e..000000000 --- a/ide/winvs/echoserver/echoserver.vcxproj.user +++ /dev/null @@ -1,19 +0,0 @@ - - - - WindowsLocalDebugger - PATH=$(wolfCryptDllDebug32);%PATH% - - - WindowsLocalDebugger - PATH=$(wolfCryptDllDebug64);%PATH% - - - WindowsLocalDebugger - PATH=$(wolfCryptDllRelease32);%PATH% - - - WindowsLocalDebugger - PATH=$(wolfCryptDllRelease64);%PATH% - - diff --git a/ide/winvs/include.am b/ide/winvs/include.am index 647adaf6d..df1990fc4 100644 --- a/ide/winvs/include.am +++ b/ide/winvs/include.am @@ -7,14 +7,9 @@ EXTRA_DIST+= ide/winvs/wolfssh.sln EXTRA_DIST+= ide/winvs/wolfssh.props EXTRA_DIST+= ide/winvs/wolfssh/wolfssh.vcxproj EXTRA_DIST+= ide/winvs/api-test/api-test.vcxproj -EXTRA_DIST+= ide/winvs/api-test/api-test.vcxproj.user EXTRA_DIST+= ide/winvs/unit-test/unit-test.vcxproj -EXTRA_DIST+= ide/winvs/unit-test/unit-test.vcxproj.user EXTRA_DIST+= ide/winvs/client/client.vcxproj -EXTRA_DIST+= ide/winvs/client/client.vcxproj.user EXTRA_DIST+= ide/winvs/echoserver/echoserver.vcxproj -EXTRA_DIST+= ide/winvs/echoserver/echoserver.vcxproj.user EXTRA_DIST+= ide/winvs/testsuite/testsuite.vcxproj -EXTRA_DIST+= ide/winvs/testsuite/testsuite.vcxproj.user EXTRA_DIST+= ide/winvs/wolfsftp-client/wolfsftp-client.vcxproj -EXTRA_DIST+= ide/winvs/wolfsftp-client/wolfsftp-client.vcxproj.user +EXTRA_DIST+= ide/winvs/wolfsshd/wolfsshd.vcxproj diff --git a/ide/winvs/testsuite/testsuite.vcxproj.user b/ide/winvs/testsuite/testsuite.vcxproj.user deleted file mode 100644 index 23a5dce7e..000000000 --- a/ide/winvs/testsuite/testsuite.vcxproj.user +++ /dev/null @@ -1,19 +0,0 @@ - - - - WindowsLocalDebugger - PATH=$(wolfCryptDllDebug32);%PATH% - - - WindowsLocalDebugger - PATH=$(wolfCryptDllDebug64);%PATH% - - - WindowsLocalDebugger - PATH=$(wolfCryptDllRelease32);%PATH% - - - WindowsLocalDebugger - PATH=$(wolfCryptDllRelease64);%PATH% - - diff --git a/ide/winvs/unit-test/unit-test.vcxproj.user b/ide/winvs/unit-test/unit-test.vcxproj.user deleted file mode 100644 index 23a5dce7e..000000000 --- a/ide/winvs/unit-test/unit-test.vcxproj.user +++ /dev/null @@ -1,19 +0,0 @@ - - - - WindowsLocalDebugger - PATH=$(wolfCryptDllDebug32);%PATH% - - - WindowsLocalDebugger - PATH=$(wolfCryptDllDebug64);%PATH% - - - WindowsLocalDebugger - PATH=$(wolfCryptDllRelease32);%PATH% - - - WindowsLocalDebugger - PATH=$(wolfCryptDllRelease64);%PATH% - - diff --git a/ide/winvs/user_settings.h b/ide/winvs/user_settings.h index 0d1fa1f07..d923629bf 100644 --- a/ide/winvs/user_settings.h +++ b/ide/winvs/user_settings.h @@ -13,6 +13,7 @@ #define HAVE_AESGCM #define HAVE_HASHDRBG #define WOLFSSL_AES_COUNTER +#define WOLFSSL_AES_DIRECT #define WOLFSSL_SHA384 #define WOLFSSL_SHA512 #define NO_PSK @@ -23,9 +24,58 @@ #define NO_MD4 #define WC_RSA_BLINDING #define WOLFSSL_PUBLIC_MP -#define SINGLE_THREADED #define WC_NO_HARDEN #define WOLFSSH_TERM +#ifndef WOLFSSH_TERM + /* Threading is needed for opening a psuedo terminal in the examples */ + #define SINGLE_THREADED +#endif + +/* adding X509 support */ +#if 0 + /* Uses CertManager which is in the TLS layer */ + #undef WOLFCRYPT_ONLY + + #undef WOLFSSL_CERT_GEN + #define WOLFSSL_CERT_GEN + + /* Used for comparing IP of peer with IP found in certificate */ + #undef WOLFSSL_IP_ALT_NAME + #define WOLFSSL_IP_ALT_NAME + + #undef HAVE_TLS_EXTENSIONS + #define HAVE_TLS_EXTENSIONS + + #undef OPENSSL_ALL + #define OPENSSL_ALL + + /* Turn off additional FPKI support checks (Federal PKI) on certificates */ + #undef WOLFSSH_NO_FPKI + #define WOLFSSH_NO_FPKI + + #undef WOLFSSH_CERTS + #define WOLFSSH_CERTS +#endif + + +/* default SSHD options */ +#if 0 + #undef WOLFSSH_SSHD + #define WOLFSSH_SSHD + + /* handle shell connections */ + #undef WOLFSSH_SHELL + #define WOLFSSH_SHELL + + /* handle SCP connection requests */ + #undef WOLFSSH_SCP + #define WOLFSSH_SCP + + /* handle SFTP connection requests */ + #undef WOLFSSH_SFTP + #define WOLFSSH_SFTP + +#endif #endif /* _WIN_USER_SETTINGS_H_ */ diff --git a/ide/winvs/wolfsftp-client/wolfsftp-client.vcxproj.user b/ide/winvs/wolfsftp-client/wolfsftp-client.vcxproj.user deleted file mode 100644 index ee6c1e37c..000000000 --- a/ide/winvs/wolfsftp-client/wolfsftp-client.vcxproj.user +++ /dev/null @@ -1,19 +0,0 @@ - - - - WindowsLocalDebugger - PATH=$(wolfCryptDllDebug32);%PATH% - - - WindowsLocalDebugger - PATH=$(wolfCryptDllDebug64);%PATH% - - - WindowsLocalDebugger - PATH=$(wolfCryptDllRelease32);%PATH% - - - WindowsLocalDebugger - PATH=$(wolfCryptDllRelease64);%PATH% - - diff --git a/ide/winvs/wolfssh.sln b/ide/winvs/wolfssh.sln index 91cb79d4d..2b9488400 100644 --- a/ide/winvs/wolfssh.sln +++ b/ide/winvs/wolfssh.sln @@ -21,6 +21,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testsuite", "testsuite\test EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wolfsftp-client", "wolfsftp-client\wolfsftp-client.vcxproj", "{8DD810D6-159B-4C40-B682-FCA11F9B3680}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wolfsshd", "wolfsshd\wolfsshd.vcxproj", "{9A31DAB4-6292-4ECB-BE35-C60D925C571E}" + ProjectSection(ProjectDependencies) = postProject + {7C2CCF0D-A155-4914-BD1C-9A47C0530E65} = {7C2CCF0D-A155-4914-BD1C-9A47C0530E65} + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -135,12 +140,28 @@ Global {8DD810D6-159B-4C40-B682-FCA11F9B3680}.DLL Debug|x64.Build.0 = Debug|x64 {8DD810D6-159B-4C40-B682-FCA11F9B3680}.DLL Release|Win32.ActiveCfg = DLL Release|Win32 {8DD810D6-159B-4C40-B682-FCA11F9B3680}.DLL Release|Win32.Build.0 = DLL Release|Win32 - {8DD810D6-159B-4C40-B682-FCA11F9B3680}.DLL Release|x64.ActiveCfg = Release|x64 - {8DD810D6-159B-4C40-B682-FCA11F9B3680}.DLL Release|x64.Build.0 = Release|x64 + {8DD810D6-159B-4C40-B682-FCA11F9B3680}.DLL Release|x64.ActiveCfg = DLL Release|x64 + {8DD810D6-159B-4C40-B682-FCA11F9B3680}.DLL Release|x64.Build.0 = DLL Release|x64 {8DD810D6-159B-4C40-B682-FCA11F9B3680}.Release|Win32.ActiveCfg = Release|Win32 {8DD810D6-159B-4C40-B682-FCA11F9B3680}.Release|Win32.Build.0 = Release|Win32 {8DD810D6-159B-4C40-B682-FCA11F9B3680}.Release|x64.ActiveCfg = Release|x64 {8DD810D6-159B-4C40-B682-FCA11F9B3680}.Release|x64.Build.0 = Release|x64 + {9A31DAB4-6292-4ECB-BE35-C60D925C571E}.Debug|Win32.ActiveCfg = Debug|Win32 + {9A31DAB4-6292-4ECB-BE35-C60D925C571E}.Debug|Win32.Build.0 = Debug|Win32 + {9A31DAB4-6292-4ECB-BE35-C60D925C571E}.Debug|x64.ActiveCfg = Debug|x64 + {9A31DAB4-6292-4ECB-BE35-C60D925C571E}.Debug|x64.Build.0 = Debug|x64 + {9A31DAB4-6292-4ECB-BE35-C60D925C571E}.DLL Debug|Win32.ActiveCfg = Debug|Win32 + {9A31DAB4-6292-4ECB-BE35-C60D925C571E}.DLL Debug|Win32.Build.0 = Debug|Win32 + {9A31DAB4-6292-4ECB-BE35-C60D925C571E}.DLL Debug|x64.ActiveCfg = Debug|x64 + {9A31DAB4-6292-4ECB-BE35-C60D925C571E}.DLL Debug|x64.Build.0 = Debug|x64 + {9A31DAB4-6292-4ECB-BE35-C60D925C571E}.DLL Release|Win32.ActiveCfg = Release|Win32 + {9A31DAB4-6292-4ECB-BE35-C60D925C571E}.DLL Release|Win32.Build.0 = Release|Win32 + {9A31DAB4-6292-4ECB-BE35-C60D925C571E}.DLL Release|x64.ActiveCfg = DLL Release|x64 + {9A31DAB4-6292-4ECB-BE35-C60D925C571E}.DLL Release|x64.Build.0 = DLL Release|x64 + {9A31DAB4-6292-4ECB-BE35-C60D925C571E}.Release|Win32.ActiveCfg = Release|Win32 + {9A31DAB4-6292-4ECB-BE35-C60D925C571E}.Release|Win32.Build.0 = Release|Win32 + {9A31DAB4-6292-4ECB-BE35-C60D925C571E}.Release|x64.ActiveCfg = Release|x64 + {9A31DAB4-6292-4ECB-BE35-C60D925C571E}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/ide/winvs/wolfssh/wolfssh.vcxproj b/ide/winvs/wolfssh/wolfssh.vcxproj index ab5ea5ca3..7cbf362f9 100644 --- a/ide/winvs/wolfssh/wolfssh.vcxproj +++ b/ide/winvs/wolfssh/wolfssh.vcxproj @@ -35,12 +35,14 @@ + + @@ -313,4 +315,4 @@ - + \ No newline at end of file diff --git a/ide/winvs/wolfsshd/wolfsshd.vcxproj b/ide/winvs/wolfsshd/wolfsshd.vcxproj new file mode 100644 index 000000000..645db1efc --- /dev/null +++ b/ide/winvs/wolfsshd/wolfsshd.vcxproj @@ -0,0 +1,202 @@ + + + + + Debug + Win32 + + + DLL Release + Win32 + + + DLL Release + x64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + {7c2ccf0d-a155-4914-bd1c-9a47c0530e65} + + + + 16.0 + Win32Proj + {9a31dab4-6292-4ecb-be35-c60d925c571e} + wolfsshd + + + + Application + true + v100 + Unicode + + + Application + false + v100 + true + Unicode + + + Application + true + v100 + Unicode + + + Application + false + v100 + true + Unicode + + + v100 + + + v100 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)$(Configuration)\$(Platform)\ + $(Configuration)\$(Platform)\ + + + $(SolutionDir)$(Configuration)\$(Platform)\ + $(Configuration)\$(Platform)\ + + + $(SolutionDir)$(Configuration)\$(Platform)\ + $(Configuration)\$(Platform)\ + false + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + ..\..\..\..\wolfssl\Debug\Win32;..\Debug\Win32 + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + ..\..\..\..\wolfssl\Release\Win32;..\Release\Win32 + + + + + Level3 + true + _CRT_SECURE_NO_DEPRECATE;WOLFSSL_USER_SETTINGS;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..;..\..\..;$(wolfCryptDir);..\..\..\apps\wolfsshd\;%(AdditionalIncludeDirectories) + + + Console + true + ..\..\..\..\wolfssl\Debug\x64;..\Debug\x64 + wolfssl.lib;ws2_32.lib;secur32.lib;userenv.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + + + + + Level3 + true + true + true + WOLFSSL_USER_SETTINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + ..;..\..\..;$(wolfCryptDir);..\..\..\apps\wolfsshd\;%(AdditionalIncludeDirectories) + + + Console + true + true + true + wolfssl.lib;ws2_32.lib;secur32.lib;userenv.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + ..\..\..\..\wolfssl\Release\x64;..\Release\x64 + + + + + WOLFSSL_USER_SETTINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + ..;..\..\..;$(wolfCryptDir);..\..\..\apps\wolfsshd\;%(AdditionalIncludeDirectories) + true + true + Level3 + + + wolfssl.lib;ws2_32.lib;secur32.lib;userenv.lib;$(CoreLibraryDependencies);%(AdditionalDependencies) + $(wolfCryptDLLRelease64) + true + true + true + + + + + + \ No newline at end of file diff --git a/src/internal.c b/src/internal.c index 54c969080..1c42618b3 100644 --- a/src/internal.c +++ b/src/internal.c @@ -151,6 +151,9 @@ const char* GetErrorString(int err) return "No wolfSSH strings available"; #else switch (err) { + case WS_SUCCESS: + return "no error"; + case WS_ERROR: return "general function failure"; @@ -576,6 +579,7 @@ WOLFSSH_CTX* CtxInit(WOLFSSH_CTX* ctx, byte side, void* heap) #endif /* WOLFSSH_CERTS */ ctx->windowSz = DEFAULT_WINDOW_SZ; ctx->maxPacketSz = DEFAULT_MAX_PACKET_SZ; + ctx->sshProtoIdStr = sshProtoIdStr; count = (word32)(sizeof(ctx->privateKey) / sizeof(ctx->privateKey[0])); @@ -626,6 +630,35 @@ void CtxResourceFree(WOLFSSH_CTX* ctx) } +#ifdef WOLFSSH_TERM +/* default terminal resize handling callbacks */ + +#if defined(USE_WINDOWS_API) && defined(WOLFSSH_SSHD) +static int WS_WindowsTermResize(WOLFSSH* ssh, word32 col, word32 row, word32 colP, + word32 rowP, void* usrCtx) +{ + HPCON* term = (HPCON*)usrCtx; + int ret = WS_SUCCESS; + + if (term != NULL) { + HRESULT ret; + COORD sz; + + sz.X = col; + sz.Y = row; + ret = ResizePseudoConsole(*term, sz); + if (ret != S_OK) { + WLOG(WS_LOG_ERROR, "Issue with pseudo console resize"); + ret = WS_FATAL_ERROR; + } + } + + return ret; +} +#endif + +#endif /* WOLFSSH_TERM */ + WOLFSSH* SshInit(WOLFSSH* ssh, WOLFSSH_CTX* ctx) { #if defined(STM32F2) || defined(STM32F4) || defined(FREESCALE_MQX) @@ -718,6 +751,12 @@ WOLFSSH* SshInit(WOLFSSH* ssh, WOLFSSH_CTX* ctx) ssh->agentEnabled = ctx->agentEnabled; #endif +#ifdef WOLFSSH_TERM + #if defined(USE_WINDOWS_API) && defined(WOLFSSH_SSHD) + ssh->termResizeCb = WS_WindowsTermResize; + #endif +#endif + if (BufferInit(&ssh->inputBuffer, 0, ctx->heap) != WS_SUCCESS || BufferInit(&ssh->outputBuffer, 0, ctx->heap) != WS_SUCCESS || BufferInit(&ssh->extDataBuffer, 0, ctx->heap) != WS_SUCCESS) { @@ -2260,8 +2299,14 @@ static int GetInputData(WOLFSSH* ssh, word32 size) /* Take into account the data already in the buffer. Update size * for what is missing in the request. */ - word32 haveDataSz = ssh->inputBuffer.length - ssh->inputBuffer.idx; + word32 haveDataSz; + /* reset want read state before attempting to read */ + if (ssh->error == WS_WANT_READ) { + ssh->error = 0; + } + + haveDataSz = ssh->inputBuffer.length - ssh->inputBuffer.idx; if (haveDataSz >= size) { WLOG(WS_LOG_INFO, "GID: have enough already, return early"); return WS_SUCCESS; @@ -3230,13 +3275,13 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) if (ret == WS_SUCCESS) { byte SSH_PROTO_EOL_SZ = 2; - strSz = (word32)WSTRLEN(sshProtoIdStr) - SSH_PROTO_EOL_SZ; + strSz = (word32)WSTRLEN(ssh->ctx->sshProtoIdStr) - SSH_PROTO_EOL_SZ; c32toa(strSz, scratchLen); ret = HashUpdate(hash, hashId, scratchLen, LENGTH_SZ); } if (ret == WS_SUCCESS) { - ret = HashUpdate(hash, hashId, (const byte*)sshProtoIdStr, strSz); + ret = HashUpdate(hash, hashId, (const byte*)ssh->ctx->sshProtoIdStr, strSz); } if (ret == WS_SUCCESS) { @@ -4187,7 +4232,7 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) /* Replace the concatenated shared secrets with the hash. That * will become the new shared secret. */ if (ret == 0) { - sharedSecretHashSz = wc_HashGetDigestSize(enmhashId); + sharedSecretHashSz = wc_HashGetDigestSize(hashId); sharedSecretHash = (byte *)WMALLOC(sharedSecretHashSz, ssh->ctx->heap, DYNTYPE_PRIVKEY); @@ -4197,7 +4242,7 @@ static int DoKexDhReply(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) } if (ret == 0) { - ret = wc_Hash(enmhashId, ssh->k, ssh->kSz, sharedSecretHash, + ret = wc_Hash(hashId, ssh->k, ssh->kSz, sharedSecretHash, sharedSecretHashSz); } @@ -4973,6 +5018,8 @@ static int DoUserAuthRequestPassword(WOLFSSH* ssh, WS_UserAuthData* authData, word32 begin; WS_UserAuthData_Password* pw = NULL; int ret = WS_SUCCESS; + int authFailure = 0; + byte partialSuccess = 0; WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestPassword()"); @@ -5015,36 +5062,43 @@ static int DoUserAuthRequestPassword(WOLFSSH* ssh, WS_UserAuthData* authData, ret = ssh->ctx->userAuthCb(WOLFSSH_USERAUTH_PASSWORD, authData, ssh->userAuthCtx); if (ret == WOLFSSH_USERAUTH_SUCCESS) { - WLOG(WS_LOG_DEBUG, "DUARPW: password check successful"); - ssh->clientState = CLIENT_USERAUTH_DONE; + WLOG(WS_LOG_DEBUG, "DUARPW: password check success"); + ret = WS_SUCCESS; + } + else if (ret == WOLFSSH_USERAUTH_PARTIAL_SUCCESS) { + WLOG(WS_LOG_DEBUG, "DUARPW: password check partial success"); + partialSuccess = 1; ret = WS_SUCCESS; } else if (ret == WOLFSSH_USERAUTH_REJECTED) { WLOG(WS_LOG_DEBUG, "DUARPW: password rejected"); #ifndef NO_FAILURE_ON_REJECTED - ret = SendUserAuthFailure(ssh, 0); - if (ret == WS_SUCCESS) - ret = WS_USER_AUTH_E; - #else - ret = WS_USER_AUTH_E; + authFailure = 1; #endif + ret = WS_USER_AUTH_E; } else { WLOG(WS_LOG_DEBUG, "DUARPW: password check failed, retry"); - ret = SendUserAuthFailure(ssh, 0); + authFailure = 1; + ret = WS_SUCCESS; } } else { WLOG(WS_LOG_DEBUG, "DUARPW: No user auth callback"); - ret = SendUserAuthFailure(ssh, 0); - if (ret == WS_SUCCESS) - ret = WS_FATAL_ERROR; + authFailure = 1; } } if (ret == WS_SUCCESS) *idx = begin; + if (authFailure || partialSuccess) { + ret = SendUserAuthFailure(ssh, partialSuccess); + } + else { + ssh->clientState = CLIENT_USERAUTH_DONE; + } + WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestPassword(), ret = %d", ret); return ret; } @@ -5728,6 +5782,7 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, word32 begin; int ret = WS_SUCCESS; int authFailure = 0; + int partialSuccess = 0; byte hasSig = 0; byte pkTypeId = ID_NONE; @@ -5834,17 +5889,49 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, 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; + #ifdef DEBUG_WOLFSSH + switch (ret) { + case WOLFSSH_USERAUTH_SUCCESS: + WLOG(WS_LOG_DEBUG, "DUARPK: user auth success"); + break; + + case WOLFSSH_USERAUTH_INVALID_PUBLICKEY: + WLOG(WS_LOG_DEBUG, "DUARPK: client key invalid"); + break; + + case WOLFSSH_USERAUTH_INVALID_USER: + WLOG(WS_LOG_DEBUG, "DUARPK: public key user rejected"); + break; + + case WOLFSSH_USERAUTH_FAILURE: + WLOG(WS_LOG_DEBUG, "DUARPK: public key general failure"); + break; + + case WOLFSSH_USERAUTH_INVALID_AUTHTYPE: + WLOG(WS_LOG_DEBUG, "DUARPK: public key invalid auth type"); + break; + + case WOLFSSH_USERAUTH_REJECTED: + WLOG(WS_LOG_DEBUG, "DUARPK: public key rejected"); + break; + + case WOLFSSH_USERAUTH_PARTIAL_SUCCESS: + WLOG(WS_LOG_DEBUG, "DUARPK: user auth partial success"); + break; + + default: + WLOG(WS_LOG_DEBUG, + "Unexpected return value from Auth callback"); } - else if (ret == WOLFSSH_USERAUTH_INVALID_PUBLICKEY) { - WLOG(WS_LOG_DEBUG, "DUARPK: client key rejected"); - authFailure = 1; - ret = WS_SUCCESS; + #endif + + if (ret == WOLFSSH_USERAUTH_PARTIAL_SUCCESS) { + partialSuccess = 1; } - else { + else if (ret != WOLFSSH_USERAUTH_SUCCESS) { authFailure = 1; } + ret = WS_SUCCESS; } else { WLOG(WS_LOG_DEBUG, "DUARPK: no userauth callback set"); @@ -5982,8 +6069,13 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, } } - if (ret == WS_SUCCESS && authFailure) { - ret = SendUserAuthFailure(ssh, 0); + if (ret == WS_SUCCESS) { + if (authFailure) { + ret = SendUserAuthFailure(ssh, 0); + } + else if (partialSuccess && hasSig) { + ret = SendUserAuthFailure(ssh, 1); + } } WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestPublicKey(), ret = %d", ret); @@ -6358,6 +6450,7 @@ static int DoChannelOpen(WOLFSSH* ssh, #endif /* WOLFSSH_FWD */ WOLFSSH_CHANNEL* newChannel = NULL; int ret = WS_SUCCESS; + word32 fail_reason = OPEN_OK; WLOG(WS_LOG_DEBUG, "Entering DoChannelOpen()"); @@ -6388,6 +6481,10 @@ static int DoChannelOpen(WOLFSSH* ssh, typeId = NameToId(type, typeSz); switch (typeId) { case ID_CHANTYPE_SESSION: + if (ssh->channelListSz >= 1) { + ret = WS_INVALID_CHANID; + fail_reason = OPEN_ADMINISTRATIVELY_PROHIBITED; + } break; #ifdef WOLFSSH_FWD case ID_CHANTYPE_TCPIP_DIRECT: @@ -6410,6 +6507,7 @@ static int DoChannelOpen(WOLFSSH* ssh, #endif default: ret = WS_INVALID_CHANTYPE; + fail_reason = OPEN_UNKNOWN_CHANNEL_TYPE; } } @@ -6418,8 +6516,10 @@ static int DoChannelOpen(WOLFSSH* ssh, newChannel = ChannelNew(ssh, typeId, ssh->ctx->windowSz, ssh->ctx->maxPacketSz); - if (newChannel == NULL) + if (newChannel == NULL) { ret = WS_RESOURCE_E; + fail_reason = OPEN_RESOURCE_SHORTAGE; + } else { ChannelUpdatePeer(newChannel, peerChannelId, peerInitialWindowSz, peerMaxPacketSz); @@ -6446,8 +6546,24 @@ static int DoChannelOpen(WOLFSSH* ssh, } } - if (ret == WS_SUCCESS) + if (ret == WS_SUCCESS) { ret = SendChannelOpenConf(ssh, newChannel); + } + else { + const char *description = NULL; + + if (fail_reason == OPEN_ADMINISTRATIVELY_PROHIBITED) + description = "Each session cannot have more than one channel open."; + else if (fail_reason == OPEN_UNKNOWN_CHANNEL_TYPE) + description = "Channel type not supported."; + else if (fail_reason == OPEN_RESOURCE_SHORTAGE) + description = "Not enough resources."; + + if (description != NULL) + ret = SendChannelOpenFail(ssh, peerChannelId, fail_reason, description, "en"); + else + ret = SendRequestSuccess(ssh, 0); + } #ifdef WOLFSSH_FWD /* ChannelUpdateForward makes new host and origin buffer */ @@ -6675,7 +6791,7 @@ static int DoChannelRequest(WOLFSSH* ssh, char term[32]; word32 termSz; word32 widthChar, heightRows, widthPixels, heightPixels; - word32 modesSz; + byte opCode = 0; termSz = (word32)sizeof(term); ret = GetString(term, &termSz, buf, len, &begin); @@ -6687,8 +6803,14 @@ static int DoChannelRequest(WOLFSSH* ssh, ret = GetUint32(&widthPixels, buf, len, &begin); if (ret == WS_SUCCESS) ret = GetUint32(&heightPixels, buf, len, &begin); - if (ret == WS_SUCCESS) - ret = GetUint32(&modesSz, buf, len, &begin); + + /* iterate over op codes */ + if (ret == WS_SUCCESS && begin < len) { + do { + opCode = buf[begin]; + begin++; + } while (opCode != 0 && begin < len); + } if (ret == WS_SUCCESS) { WLOG(WS_LOG_DEBUG, " term = %s", term); @@ -6696,7 +6818,14 @@ static int DoChannelRequest(WOLFSSH* ssh, WLOG(WS_LOG_DEBUG, " heightRows = %u", heightRows); WLOG(WS_LOG_DEBUG, " widthPixels = %u", widthPixels); WLOG(WS_LOG_DEBUG, " heightPixels = %u", heightPixels); - WLOG(WS_LOG_DEBUG, " modes = %u", (modesSz - 1) / 5); + ssh->curX = widthChar; + ssh->curY = heightRows; + if (ssh->termResizeCb) { + if (ssh->termResizeCb(ssh, widthChar, heightRows, widthPixels, + heightPixels, ssh->termCtx) != WS_SUCCESS) { + ret = WS_FATAL_ERROR; + } + } } } else @@ -6746,6 +6875,34 @@ static int DoChannelRequest(WOLFSSH* ssh, WLOG(WS_LOG_AGENT, "Agent callback not set, not using."); } #endif /* WOLFSSH_AGENT */ +#ifdef WOLFSSH_SHELL + else if (WSTRNCMP(type, "window-change", typeSz) == 0) { + word32 widthChar, heightRows, widthPixels, heightPixels; + + ret = GetUint32(&widthChar, buf, len, &begin); + if (ret == WS_SUCCESS) + ret = GetUint32(&heightRows, buf, len, &begin); + if (ret == WS_SUCCESS) + ret = GetUint32(&widthPixels, buf, len, &begin); + if (ret == WS_SUCCESS) + ret = GetUint32(&heightPixels, buf, len, &begin); + + if (ret == WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, " widthChar = %u", widthChar); + WLOG(WS_LOG_DEBUG, " heightRows = %u", heightRows); + WLOG(WS_LOG_DEBUG, " widthPixels = %u", widthPixels); + WLOG(WS_LOG_DEBUG, " heightPixels = %u", heightPixels); + ssh->curX = widthChar; + ssh->curY = heightRows; + if (ssh->termResizeCb) { + if (ssh->termResizeCb(ssh, widthChar, heightRows, widthPixels, + heightPixels, ssh->termCtx) != WS_SUCCESS) { + ret = WS_FATAL_ERROR; + } + } + } + } +#endif } if (ret == WS_SUCCESS) @@ -7632,7 +7789,7 @@ int DoReceive(WOLFSSH* ssh) + peerBlockSz, ssh->inputBuffer.buffer + ssh->inputBuffer.idx + peerBlockSz, - ssh->curSz - peerBlockSz); + UINT32_SZ + ssh->curSz - peerBlockSz); } else { /* Entire packet fit in one block, don't need @@ -7728,7 +7885,7 @@ int DoProtoId(WOLFSSH* ssh) } if (WSTRNCASECMP((char*)ssh->inputBuffer.buffer, - sshProtoIdStr, SSH_PROTO_SZ) == 0) { + ssh->ctx->sshProtoIdStr, SSH_PROTO_SZ) == 0) { if (ssh->ctx->side == WOLFSSH_ENDPOINT_SERVER) ssh->clientState = CLIENT_VERSION_DONE; @@ -7779,14 +7936,14 @@ int SendProtoId(WOLFSSH* ssh) ret = WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { - WLOG(WS_LOG_DEBUG, "%s", sshProtoIdStr); - sshProtoIdStrSz = (word32)WSTRLEN(sshProtoIdStr); + WLOG(WS_LOG_DEBUG, "%s", ssh->ctx->sshProtoIdStr); + sshProtoIdStrSz = (word32)WSTRLEN(ssh->ctx->sshProtoIdStr); ret = GrowBuffer(&ssh->outputBuffer, sshProtoIdStrSz); } if (ret == WS_SUCCESS) { WMEMCPY(ssh->outputBuffer.buffer + ssh->outputBuffer.length, - sshProtoIdStr, sshProtoIdStrSz); + ssh->ctx->sshProtoIdStr, sshProtoIdStrSz); ssh->outputBuffer.length += sshProtoIdStrSz; ret = wolfSSH_SendPacket(ssh); } @@ -8338,10 +8495,10 @@ struct wolfSSH_sigKeyBlockFull { #ifndef WOLFSSH_NO_RSA struct { RsaKey key; - byte e[257]; + byte e[1025]; word32 eSz; byte ePad; - byte n[257]; + byte n[1025]; word32 nSz; byte nPad; } rsa; @@ -8474,6 +8631,46 @@ static int BuildRFC6187Info(WOLFSSH* ssh, int pubKeyID, #endif /* WOLFSSH_CERTS */ +#ifndef WOLFSSH_NO_DH +static int GetDHPrimeGroup(int kexId, const byte** primeGroup, + word32* primeGroupSz, const byte** generator, word32* generatorSz) +{ + int ret = WS_SUCCESS; + + switch (kexId) { + #ifndef WOLFSSH_NO_DH_GROUP1_SHA1 + case ID_DH_GROUP1_SHA1: + *primeGroup = dhPrimeGroup1; + *primeGroupSz = dhPrimeGroup1Sz; + *generator = dhGenerator; + *generatorSz = dhGeneratorSz; + break; + #endif + #ifndef WOLFSSH_NO_DH_GROUP14_SHA1 + case ID_DH_GROUP14_SHA1: + *primeGroup = dhPrimeGroup14; + *primeGroupSz = dhPrimeGroup14Sz; + *generator = dhGenerator; + *generatorSz = dhGeneratorSz; + break; + #endif + #ifndef WOLFSSH_NO_DH_GEX_SHA256 + case ID_DH_GEX_SHA256: + *primeGroup = dhPrimeGroup14; + *primeGroupSz = dhPrimeGroup14Sz; + *generator = dhGenerator; + *generatorSz = dhGeneratorSz; + break; + #endif + default: + ret = WS_INVALID_ALGO_ID; + } + + return ret; +} +#endif /* !WOLFSSH_NO_DH */ + + /* Sets the signing key and hashes in the public key * returns WS_SUCCESS on success */ static int SendKexGetSigningKey(WOLFSSH* ssh, @@ -8717,6 +8914,11 @@ static int SendKexGetSigningKey(WOLFSSH* ssh, if (ssh->handshake->kexId == ID_DH_GEX_SHA256) { byte primeGroupPad = 0, generatorPad = 0; + if (GetDHPrimeGroup(ssh->handshake->kexId, &primeGroup, + &primeGroupSz, &generator, &generatorSz) != WS_SUCCESS) { + ret = WS_BAD_ARGUMENT; + } + /* Hash in the client's requested minimum key size. */ if (ret == 0) { c32toa(ssh->handshake->dhGexMinSz, scratchLen); @@ -8991,35 +9193,13 @@ int SendKexDhReply(WOLFSSH* ssh) y_ptr = y_s; #endif if (ret == WS_SUCCESS) { - switch (ssh->handshake->kexId) { - #ifndef WOLFSSH_NO_DH_GROUP1_SHA1 - case ID_DH_GROUP1_SHA1: - primeGroup = dhPrimeGroup1; - primeGroupSz = dhPrimeGroup1Sz; - generator = dhGenerator; - generatorSz = dhGeneratorSz; - break; - #endif - #ifndef WOLFSSH_NO_DH_GROUP14_SHA1 - case ID_DH_GROUP14_SHA1: - primeGroup = dhPrimeGroup14; - primeGroupSz = dhPrimeGroup14Sz; - generator = dhGenerator; - generatorSz = dhGeneratorSz; - break; - #endif - #ifndef WOLFSSH_NO_DH_GEX_SHA256 - case ID_DH_GEX_SHA256: - primeGroup = dhPrimeGroup14; - primeGroupSz = dhPrimeGroup14Sz; - generator = dhGenerator; - generatorSz = dhGeneratorSz; - msgId = MSGID_KEXDH_GEX_REPLY; - break; - #endif - default: - ret = WS_INVALID_ALGO_ID; - } + ret = GetDHPrimeGroup(ssh->handshake->kexId, &primeGroup, + &primeGroupSz, &generator, &generatorSz); + #ifndef WOLFSSH_NO_DH_GEX_SHA256 + if (ssh->handshake->kexId == ID_DH_GEX_SHA256) + msgId = MSGID_KEXDH_GEX_REPLY; + #endif + if (ret == WS_SUCCESS) { ret = wc_InitDhKey(privKey); } @@ -9206,7 +9386,7 @@ int SendKexDhReply(WOLFSSH* ssh) ret = wc_ecc_shared_secret(privKey, pubKey, ssh->k + kem->length_shared_secret, &tmp_kSz); PRIVATE_KEY_LOCK(); - ssh->kSz = kem->length_shared_secret + tmp_kSz; + ssh->kSz = (word32)kem->length_shared_secret + tmp_kSz; } wc_ecc_free(privKey); wc_ecc_free(pubKey); @@ -12163,6 +12343,58 @@ int SendChannelOpenConf(WOLFSSH* ssh, WOLFSSH_CHANNEL* channel) return ret; } +int SendChannelOpenFail(WOLFSSH* ssh, word32 channel, word32 reason, const char *description, const char *language) +{ + byte* output; + word32 idx; + word32 descriptionSz = (word32)WSTRLEN(description); + word32 languageSz = (word32)WSTRLEN(language); + int ret = WS_SUCCESS; + + WLOG(WS_LOG_DEBUG, "Entering SendChannelOpenFail()"); + + if (ssh == NULL) + ret = WS_BAD_ARGUMENT; + + if (ret == WS_SUCCESS) { + WLOG(WS_LOG_INFO, " channelId = %u", channel); + WLOG(WS_LOG_INFO, " reason = %u", reason); + WLOG(WS_LOG_INFO, " description = %s", description); + WLOG(WS_LOG_INFO, " language = %s", language); + } + + if (ret == WS_SUCCESS) + ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ + UINT32_SZ + LENGTH_SZ + descriptionSz + LENGTH_SZ + languageSz); + + if (ret == WS_SUCCESS) { + output = ssh->outputBuffer.buffer; + idx = ssh->outputBuffer.length; + + output[idx++] = MSGID_CHANNEL_OPEN_FAIL; + c32toa(channel, output + idx); + idx += UINT32_SZ; + c32toa(reason, output + idx); + idx += UINT32_SZ; + c32toa(descriptionSz, output + idx); + idx += UINT32_SZ; + WMEMCPY(output + idx, description, descriptionSz); + idx += descriptionSz; + c32toa(languageSz, output + idx); + idx += UINT32_SZ; + WMEMCPY(output + idx, language, languageSz); + idx += languageSz; + + ssh->outputBuffer.length = idx; + + ret = BundlePacket(ssh); + } + + if (ret == WS_SUCCESS) + ret = wolfSSH_SendPacket(ssh); + + WLOG(WS_LOG_DEBUG, "Leaving SendChannelOpenFail(), ret = %d", ret); + return ret; +} int SendChannelEof(WOLFSSH* ssh, word32 peerChannelId) { @@ -12775,6 +13007,97 @@ static int CreateMode(WOLFSSH* ssh, byte* mode) } +int SendChannelTerminalResize(WOLFSSH* ssh, word32 columns, word32 rows, + word32 widthPixels, word32 heightPixels) +{ + int ret = WS_SUCCESS; + byte* output; + word32 idx; + WOLFSSH_CHANNEL* channel = NULL; + const char* cType = "window-change"; + word32 typeSz = 0; + + if (ret == WS_SUCCESS) { + channel = ChannelFind(ssh, + ssh->defaultPeerChannelId, WS_CHANNEL_ID_PEER); + if (channel == NULL) + ret = WS_INVALID_CHANID; + } + + if (ret == WS_SUCCESS) { + typeSz = (word32)WSTRLEN(cType); + ret = PreparePacket(ssh, MSG_ID_SZ + UINT32_SZ + LENGTH_SZ + + typeSz + BOOLEAN_SZ + (4 * UINT32_SZ)); + } + + if (ret == WS_SUCCESS) { + output = ssh->outputBuffer.buffer; + idx = ssh->outputBuffer.length; + + output[idx++] = MSGID_CHANNEL_REQUEST; + c32toa(channel->peerChannel, output + idx); + idx += UINT32_SZ; + c32toa(typeSz, output + idx); + idx += LENGTH_SZ; + WMEMCPY(output + idx, cType, typeSz); + idx += typeSz; + output[idx++] = 0; + + c32toa(columns, output + idx); + idx += UINT32_SZ; + c32toa(rows, output + idx); + idx += UINT32_SZ; + c32toa(widthPixels, output + idx); + idx += UINT32_SZ; + c32toa(heightPixels, output + idx); + idx += UINT32_SZ; + + ssh->outputBuffer.length = idx; + + WLOG(WS_LOG_INFO, "Sending Channel Request: "); + WLOG(WS_LOG_INFO, " channelId = %u", channel->peerChannel); + WLOG(WS_LOG_INFO, " type = %s", cType); + WLOG(WS_LOG_INFO, " wantReply = %u", 0); + + ret = BundlePacket(ssh); + } + + if (ret == WS_SUCCESS) + ret = wolfSSH_SendPacket(ssh); + + return ret; +} + + +#ifdef __linux__ +#ifdef HAVE_PTY_H + #include +#endif +#endif + +static void GetTerminalSize(word32* width, word32* height) +{ +#ifdef __linux__ + struct winsize windowSize = { 0,0,0,0 }; + + ioctl(STDOUT_FILENO, TIOCGWINSZ, &windowSize); + *width = (word32)windowSize.ws_col; + *height = (word32)windowSize.ws_row; +#elif defined(_MSC_VER) + CONSOLE_SCREEN_BUFFER_INFO cs; + + if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cs) != 0) { + *width = cs.srWindow.Right - cs.srWindow.Left + 1; + *height = cs.srWindow.Bottom - cs.srWindow.Top + 1; + } +#else + /* sane defaults for terminal size if not yet supported */ + *width = 80; + *height = 24; +#endif +} + + /* sends request for pseudo-terminal (rfc 4254) * returns WS_SUCCESS on success */ int SendChannelTerminalRequest(WOLFSSH* ssh) @@ -12787,7 +13110,7 @@ int SendChannelTerminalRequest(WOLFSSH* ssh) const char envVar[] = "xterm"; byte mode[4096]; word32 envSz, typeSz, modeSz; - word32 w = 80, h = 24; + word32 w, h; word32 pxW = 0, pxH = 0; WLOG(WS_LOG_DEBUG, "Entering SendChannelTerminalRequest()"); @@ -12795,6 +13118,7 @@ int SendChannelTerminalRequest(WOLFSSH* ssh) if (ssh == NULL) ret = WS_BAD_ARGUMENT; + GetTerminalSize(&w, &h); envSz = (word32)WSTRLEN(envVar); typeSz = (word32)WSTRLEN(cType); modeSz = CreateMode(ssh, mode); @@ -13216,9 +13540,13 @@ int wolfSSH_oct2dec(WOLFSSH* ssh, byte* oct, word32 octSz) /* addend1 += addend2 */ void AddAssign64(word32* addend1, word32 addend2) { - word32 tmp = addend1[0]; - if ((addend1[0] += addend2) < tmp) + if (addend1[0] > (WOLFSSL_MAX_32BIT - addend2)) { addend1[1]++; + addend1[0] = addend2 - (WOLFSSL_MAX_32BIT- addend1[0]); + } + else { + addend1[0] += addend2; + } } #endif /* WOLFSSH_SFTP */ diff --git a/src/port.c b/src/port.c index f8d6c0462..543c6dbb0 100644 --- a/src/port.c +++ b/src/port.c @@ -66,13 +66,13 @@ int wfopen(WFILE** f, const char* filename, const char* mode) } if (filename != NULL && f != NULL) { - if ((**f = WOPEN(filename, m, 0)) < 0) { + if ((**f = WOPEN(ssh->fs, filename, m, 0)) < 0) { return **f; } /* fopen defaults to normal */ if (NU_Set_Attributes(filename, 0) != NU_SUCCESS) { - WCLOSE(**f); + WCLOSE(ssh->fs, **f); return 1; } return 0; diff --git a/src/ssh.c b/src/ssh.c index 2db5e340c..75bd8d1fc 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -1316,6 +1316,41 @@ void* wolfSSH_GetPublicKeyCheckCtx(WOLFSSH* ssh) } +/* Used to resize terminal window with shell connections + * returns WS_SUCCESS on success */ +int wolfSSH_ChangeTerminalSize(WOLFSSH* ssh, word32 columns, word32 rows, + word32 widthPixels, word32 heightPixels) +{ + int ret = WS_SUCCESS; + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_ChangeWindowDimension()"); + + if (ssh == NULL) + ret = WS_BAD_ARGUMENT; + + if (ret == WS_SUCCESS) { + ret = SendChannelTerminalResize(ssh, columns, rows, widthPixels, + heightPixels); + } + + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_ChangeWindowDimension(), ret = %d", + ret); + return ret; +} + + +void wolfSSH_SetTerminalResizeCb(WOLFSSH* ssh, WS_CallbackTerminalSize cb) +{ + ssh->termResizeCb = cb; +} + + +void wolfSSH_SetTerminalResizeCtx(WOLFSSH* ssh, void* usrCtx) +{ + ssh->termCtx = usrCtx; +} + + /* Used to set the channel request type sent in wolfSSH connect. The default * type set is shell if this function is not called. * @@ -1534,7 +1569,7 @@ int wolfSSH_ReadKey_buffer(const byte* in, word32 inSz, int format, } -#ifndef NO_FILESYSTEM +#if !defined(NO_FILESYSTEM) && !defined(WOLFSSH_USER_FILESYSTEM) /* Reads a key from the file name into a buffer. If the key starts with the string "ssh-rsa" or "ecdsa-sha2-nistp256", it is considered an SSH format @@ -1557,27 +1592,27 @@ int wolfSSH_ReadKey_file(const char* name, isPrivate == NULL) return WS_BAD_ARGUMENT; - ret = WFOPEN(&file, name, "rb"); + ret = WFOPEN(NULL, &file, name, "rb"); if (ret != 0 || file == WBADFILE) return WS_BAD_FILE_E; - if (WFSEEK(file, 0, WSEEK_END) != 0) { - WFCLOSE(file); + if (WFSEEK(NULL, file, 0, WSEEK_END) != 0) { + WFCLOSE(NULL, file); return WS_BAD_FILE_E; } - inSz = (word32)WFTELL(file); - WREWIND(file); + inSz = (word32)WFTELL(NULL, file); + WREWIND(NULL, file); if (inSz > WOLFSSH_MAX_FILE_SIZE || inSz == 0) { - WFCLOSE(file); + WFCLOSE(NULL, file); return WS_BAD_FILE_E; } in = (byte*)WMALLOC(inSz + 1, heap, DYNTYPE_FILE); if (in == NULL) { - WFCLOSE(file); + WFCLOSE(NULL, file); return WS_MEMORY_E; } - ret = (int)WFREAD(in, 1, inSz, file); + ret = (int)WFREAD(NULL, in, 1, inSz, file); if (ret <= 0 || (word32)ret != inSz) { ret = WS_BAD_FILE_E; } @@ -1599,7 +1634,7 @@ int wolfSSH_ReadKey_file(const char* name, out, outSz, outType, outTypeSz, heap); } - WFCLOSE(file); + WFCLOSE(ssh->fs, file); WFREE(in, heap, DYNTYPE_FILE); return ret; @@ -1628,6 +1663,16 @@ int wolfSSH_CTX_SetBanner(WOLFSSH_CTX* ctx, return WS_SUCCESS; } +int wolfSSH_CTX_SetSshProtoIdStr(WOLFSSH_CTX* ctx, + const char* protoIdStr) +{ + if (!ctx || !protoIdStr) { + return WS_BAD_ARGUMENT; + } + + ctx->sshProtoIdStr = protoIdStr; + return WS_SUCCESS; +} int wolfSSH_CTX_UsePrivateKey_buffer(WOLFSSH_CTX* ctx, const byte* in, word32 inSz, int format) diff --git a/src/wolfscp.c b/src/wolfscp.c index f3db331ef..c568ed4d0 100644 --- a/src/wolfscp.c +++ b/src/wolfscp.c @@ -53,8 +53,8 @@ #ifndef NO_FILESYSTEM static int ScpFileIsDir(ScpSendCtx* ctx); -static int ScpPushDir(ScpSendCtx* ctx, const char* path, void* heap); -static int ScpPopDir(ScpSendCtx* ctx, void* heap); +static int ScpPushDir(void *fs, ScpSendCtx* ctx, const char* path, void* heap); +static int ScpPopDir(void *fs, ScpSendCtx* ctx, void* heap); #endif const char scpError[] = "scp error: %s, %d"; @@ -1095,7 +1095,7 @@ static int ScpCheckForRename(WOLFSSH* ssh, int cmdSz) WSTRNCPY(buf, ssh->scpBasePath, cmdSz); buf[sz] = '\0'; - WSTRNCAT(buf, "/..", sizeof("/..")); + WSTRNCAT(buf, "/..", DEFAULT_SCP_MSG_SZ); idx = wolfSSH_CleanPath(ssh, buf); if (idx < 0) { @@ -1143,7 +1143,7 @@ static int ScpCheckForRename(WOLFSSH* ssh, int cmdSz) WLOG(WS_LOG_DEBUG, "scp: renaming from %s to %s", ssh->scpFileName, ssh->scpBasePath + idx); ssh->scpFileReName = ssh->scpFileName; - WSTRNCPY(ssh->scpFileName, ssh->scpBasePath + idx, sz); + WSTRNCPY(ssh->scpFileName, ssh->scpBasePath + idx, sz + 1); ssh->scpFileName[sz] = '\0'; ssh->scpFileNameSz = sz; *((char*)ssh->scpBasePath + idx) = '\0'; @@ -1158,18 +1158,20 @@ static int ScpCheckForRename(WOLFSSH* ssh, int cmdSz) * returns WS_SUCCESS on success */ static int ParseBasePathHelper(WOLFSSH* ssh, int cmdSz) { - int ret = 0; + int ret; ret = ScpCheckForRename(ssh, cmdSz); #ifndef NO_FILESYSTEM - { + if (ret == WS_SUCCESS) { ScpSendCtx ctx; WMEMSET(&ctx, 0, sizeof(ScpSendCtx)); - if (ScpPushDir(&ctx, ssh->scpBasePath, ssh->ctx->heap) != WS_SUCCESS) { + + if (ScpPushDir(ssh->fs, &ctx, ssh->scpBasePath, ssh->ctx->heap) != WS_SUCCESS) { WLOG(WS_LOG_DEBUG, "scp : issue opening base dir"); + ret = WS_FATAL_ERROR; } else { - ret = ScpPopDir(&ctx, ssh->ctx->heap); + ret = ScpPopDir(ssh->fs, &ctx, ssh->ctx->heap); if (ret == WS_SCP_DIR_STACK_EMPTY_E) { ret = WS_SUCCESS; /* is ok to empty the directory stack here */ } @@ -1261,7 +1263,8 @@ int ParseScpCommand(WOLFSSH* ssh) ssh->scpBasePath = cmd + idx; #endif ret = ParseBasePathHelper(ssh, cmdSz); - if (wolfSSH_CleanPath(ssh, (char*)ssh->scpBasePath) < 0) + if (ret == WS_SUCCESS && + wolfSSH_CleanPath(ssh, (char*)ssh->scpBasePath) < 0) ret = WS_FATAL_ERROR; } break; @@ -1766,29 +1769,42 @@ static INLINE int wolfSSH_LastError(void) return errno; } + /* set file access and modification times * Returns WS_SUCCESS on success, or negative upon error */ static int SetTimestampInfo(const char* fileName, word64 mTime, word64 aTime) { int ret = WS_SUCCESS; +#ifdef USE_WINDOWS_API + struct _utimbuf tmp; + int fd; +#else struct timeval tmp[2]; +#endif if (fileName == NULL) ret= WS_BAD_ARGUMENT; if (ret == WS_SUCCESS) { +#ifdef USE_WINDOWS_API + tmp.actime = aTime; + tmp.modtime = mTime; + _sopen_s(&fd, fileName, _O_RDWR, _SH_DENYNO, 0); + _futime(fd, &tmp); + _close(fd); +#else tmp[0].tv_sec = (time_t)aTime; tmp[0].tv_usec = 0; tmp[1].tv_sec = (time_t)mTime; tmp[1].tv_usec = 0; ret = WUTIMES(fileName, tmp); +#endif } return ret; } - /* Default SCP receive callback, called by wolfSSH when application has called * wolfSSH_accept() and a new SCP request has been received for an incomming * file or directory. @@ -1910,7 +1926,7 @@ int wsScpRecvCallback(WOLFSSH* ssh, int state, const char* basePath, } } #else - if (WCHDIR(basePath) != 0) { + if (WCHDIR(ssh->fs, basePath) != 0) { WLOG(WS_LOG_ERROR, "scp: invalid destination directory, abort"); wolfSSH_SetScpErrorMsg(ssh, "invalid destination directory"); @@ -1928,9 +1944,9 @@ int wsScpRecvCallback(WOLFSSH* ssh, int state, const char* basePath, WSTRNCAT(abslut, "/", WOLFSSH_MAX_FILENAME); WSTRNCAT(abslut, fileName, WOLFSSH_MAX_FILENAME); wolfSSH_CleanPath(ssh, abslut); - if (WFOPEN(&fp, abslut, "wb") != 0) { + if (WFOPEN(ssh->fs, &fp, abslut, "wb") != 0) { #else - if (WFOPEN(&fp, fileName, "wb") != 0) { + if (WFOPEN(ssh->fs, &fp, fileName, "wb") != 0) { #endif WLOG(WS_LOG_ERROR, "scp: unable to open file for writing, abort"); @@ -1954,11 +1970,11 @@ int wsScpRecvCallback(WOLFSSH* ssh, int state, const char* basePath, break; } /* read file, or file part */ - bytes = (word32)WFWRITE(buf, 1, bufSz, fp); + bytes = (word32)WFWRITE(ssh->fs, buf, 1, bufSz, fp); if (bytes != bufSz) { WLOG(WS_LOG_ERROR, scpError, "scp receive callback unable " "to write requested size to file", bytes); - WFCLOSE(fp); + WFCLOSE(ssh->fs, fp); ret = WS_SCP_ABORT; } else { #ifdef WOLFSCP_FLUSH @@ -1984,7 +2000,7 @@ int wsScpRecvCallback(WOLFSSH* ssh, int state, const char* basePath, (void)fsync(fileno(fp)); flush_bytes = 0; #endif - WFCLOSE(fp); + WFCLOSE(ssh->fs, fp); } /* set timestamp info */ @@ -2041,7 +2057,7 @@ int wsScpRecvCallback(WOLFSSH* ssh, int state, const char* basePath, WSTRNCAT((char*)basePath, fileName, WOLFSSH_MAX_FILENAME); wolfSSH_CleanPath(ssh, (char*)basePath); #else - if (WCHDIR(fileName) != 0) { + if (WCHDIR(ssh->fs, fileName) != 0) { WLOG(WS_LOG_ERROR, "scp: unable to cd into direcotry, abort"); wolfSSH_SetScpErrorMsg(ssh, "unable to cd into directory"); @@ -2058,7 +2074,7 @@ int wsScpRecvCallback(WOLFSSH* ssh, int state, const char* basePath, WSTRNCAT((char*)basePath, "/..", WOLFSSH_MAX_FILENAME - 1); wolfSSH_CleanPath(ssh, (char*)basePath); #else - if (WCHDIR("..") != 0) { + if (WCHDIR(ssh->fs, "..") != 0) { WLOG(WS_LOG_ERROR, "scp: unable to cd out of direcotry, abort"); wolfSSH_SetScpErrorMsg(ssh, "unable to cd out of directory"); @@ -2079,23 +2095,27 @@ int wsScpRecvCallback(WOLFSSH* ssh, int state, const char* basePath, return ret; } -static int GetFileSize(WFILE* fp, word32* fileSz) +static int _GetFileSize(void* fs, WFILE* fp, word32* fileSz) { + WOLFSSH_UNUSED(fs); + if (fp == NULL || fileSz == NULL) return WS_BAD_ARGUMENT; /* get file size */ - WFSEEK(fp, 0, WSEEK_END); - *fileSz = (word32)WFTELL(fp); - WREWIND(fp); + WFSEEK(fs, fp, 0, WSEEK_END); + *fileSz = (word32)WFTELL(fs, fp); + WREWIND(fs, fp); return WS_SUCCESS; } -static int GetFileStats(ScpSendCtx* ctx, const char* fileName, +static int GetFileStats(void *fs, ScpSendCtx* ctx, const char* fileName, word64* mTime, word64* aTime, int* fileMode) { int ret = WS_SUCCESS; + + WOLFSSH_UNUSED(fs); if (ctx == NULL || fileName == NULL || mTime == NULL || aTime == NULL || fileMode == NULL) { @@ -2103,7 +2123,23 @@ static int GetFileStats(ScpSendCtx* ctx, const char* fileName, } /* get file stats for times and mode */ - if (WSTAT(fileName, &ctx->s) < 0) { +#if defined(USE_WINDOWS_API) + BOOL error; + + error = !WS_GetFileAttributesExA(fileName, &ctx->s, NULL); + if (error) + return WS_BAD_FILE_E; + + *aTime = ((word64)ctx->s.ftLastAccessTime.dwHighDateTime << 32) | + (word64)ctx->s.ftLastAccessTime.dwLowDateTime; + *mTime = ((word64)ctx->s.ftLastWriteTime.dwHighDateTime << 32) | + (word64)ctx->s.ftLastWriteTime.dwLowDateTime; + + *fileMode = 0555 | + (ctx->s.dwFileAttributes | FILE_ATTRIBUTE_READONLY ? 0 : 0200); + *fileMode |= (ctx->s.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 0x4000 : 0; +#else + if (WSTAT(fs, fileName, &ctx->s) < 0) { ret = WS_BAD_FILE_E; #ifdef WOLFSSL_NUCLEUS if (WSTRLEN(fileName) < 4 && WSTRLEN(fileName) > 2 && @@ -2112,7 +2148,8 @@ static int GetFileStats(ScpSendCtx* ctx, const char* fileName, ret = WS_SUCCESS; } #endif - } else { + } + else { #ifdef WOLFSSL_NUCLEUS if (ctx->s.fattribute & ARDONLY) { *fileMode = 0x124; /* octal 444 */ @@ -2132,14 +2169,17 @@ static int GetFileStats(ScpSendCtx* ctx, const char* fileName, *fileMode = ctx->s.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); #endif } +#endif return ret; } /* Create new ScpDir struct for pushing on directory stack. * Return valid pointer on success, NULL on failure */ -static ScpDir* ScpNewDir(const char* path, void* heap) +static ScpDir* ScpNewDir(void *fs, const char* path, void* heap) { + WOLFSSH_UNUSED(fs); + ScpDir* entry = NULL; if (path == NULL) { @@ -2155,7 +2195,25 @@ static ScpDir* ScpNewDir(const char* path, void* heap) } entry->next = NULL; - if (WOPENDIR(NULL, heap, &entry->dir, path) != 0 +#ifdef USE_WINDOWS_API + { + char sPath[MAX_PATH]; + int isDir; + + /* add wildcard to get full directory */ + WSNPRINTF(sPath, MAX_PATH, "%s/*", path); + + entry->dir = (HANDLE)WS_FindFirstFileA(sPath, + sPath, sizeof(sPath), &isDir, heap); + if (entry->dir == INVALID_HANDLE_VALUE) { + WFREE(entry, heap, DYNTYPE_SCPDIR); + WLOG(WS_LOG_ERROR, scpError, "opendir failed on directory", + WS_INVALID_PATH_E); + return NULL; + } + } +#else + if (WOPENDIR(fs, heap, &entry->dir, path) != 0 #ifndef WOLFSSL_NUCLEUS || entry->dir == NULL #endif @@ -2165,19 +2223,19 @@ static ScpDir* ScpNewDir(const char* path, void* heap) WS_INVALID_PATH_E); return NULL; } - +#endif return entry; } /* Create and push new ScpDir on stack, append directory to ctx->dirName */ -int ScpPushDir(ScpSendCtx* ctx, const char* path, void* heap) +int ScpPushDir(void *fs, ScpSendCtx* ctx, const char* path, void* heap) { ScpDir* entry; if (ctx == NULL || path == NULL) return WS_BAD_ARGUMENT; - entry = ScpNewDir(path, heap); + entry = ScpNewDir(fs, path, heap); if (entry == NULL) { return WS_FATAL_ERROR; } @@ -2198,8 +2256,10 @@ int ScpPushDir(ScpSendCtx* ctx, const char* path, void* heap) } /* Remove top ScpDir from directory stack, remove dir from ctx->dirName */ -int ScpPopDir(ScpSendCtx* ctx, void* heap) +int ScpPopDir(void *fs, ScpSendCtx* ctx, void* heap) { + WOLFSSH_UNUSED(fs); + ScpDir* entry = NULL; int idx = 0, separator = 0; @@ -2209,7 +2269,11 @@ int ScpPopDir(ScpSendCtx* ctx, void* heap) } if (entry != NULL) { - WCLOSEDIR(&entry->dir); + #ifdef USE_WINDOWS_API + FindClose(entry->dir); + #else + WCLOSEDIR(fs, &entry->dir); + #endif WFREE(entry, heap, DYNTYPE_SCPDIR); } @@ -2235,8 +2299,10 @@ int ScpPopDir(ScpSendCtx* ctx, void* heap) /* Get next entry in directory, either file or directory, skips self (.) * and parent (..) directories, stores in ctx->entry. * Return WS_SUCCESS on success or negative upon error */ -static int FindNextDirEntry(ScpSendCtx* ctx) +static int FindNextDirEntry(void *fs, ScpSendCtx* ctx) { + WOLFSSH_UNUSED(fs); + if (ctx == NULL) return WS_BAD_ARGUMENT; @@ -2259,7 +2325,7 @@ static int FindNextDirEntry(ScpSendCtx* ctx) if (ctx->nextError == 1) { WDIR* dr; do { - dr = WREADDIR(&ctx->currentDir->dir); + dr = WREADDIR(fs, &ctx->currentDir->dir); } while (dr != NULL && (WSTRNCMP(ctx->currentDir->dir.lfname, ".", 1) == 0 || WSTRNCMP(ctx->currentDir->dir.lfname ,"..", 2) == 0)); @@ -2268,12 +2334,42 @@ static int FindNextDirEntry(ScpSendCtx* ctx) } } ctx->nextError = 1; +#elif defined(USE_WINDOWS_API) + do { + char realFileName[MAX_PATH]; + int sz; + + if (WS_FindNextFileA(ctx->currentDir->dir, + realFileName, sizeof(realFileName)) == 0) { + return WS_FATAL_ERROR; + } + + sz = (int)WSTRLEN(realFileName); + if (ctx->entry != NULL) { + WFREE(ctx->entry, NULL, DYNTYPE_SCPDIR); + ctx->entry = NULL; + } + + ctx->entry = (char*)WMALLOC(sz + 1, NULL, DYNTYPE_SCPDIR); + if (ctx->entry == NULL) { + return WS_MEMORY_E; + } + WMEMCPY(ctx->entry, realFileName, sz); + ctx->entry[sz] = '\0'; + } while ((ctx->entry != NULL) && + (((WSTRLEN(ctx->entry) == 1) && WSTRNCMP(ctx->entry, ".", 1) == 0) || + ((WSTRLEN(ctx->entry) == 2) && WSTRNCMP(ctx->entry, "..", 2) == 0))); #else do { - ctx->entry = WREADDIR(&ctx->currentDir->dir); + ctx->entry = WREADDIR(fs, &ctx->currentDir->dir); } while ((ctx->entry != NULL) && - (WSTRNCMP(ctx->entry->d_name, ".", 1) == 0 || - WSTRNCMP(ctx->entry->d_name ,"..", 2) == 0)); + ( + ((WSTRLEN(ctx->entry->d_name) == 1) && + (WSTRNCMP(ctx->entry->d_name, ".", 1) == 0)) + || + ((WSTRLEN(ctx->entry->d_name) == 2) && + (WSTRNCMP(ctx->entry->d_name ,"..", 2) == 0)) + )); #endif return WS_SUCCESS; @@ -2293,6 +2389,8 @@ int ScpFileIsDir(ScpSendCtx* ctx) { #ifdef WOLFSSL_NUCLEUS return (ctx->s.fattribute & ADIRENT); +#elif defined(USE_WINDOWS_API) + return (ctx->s.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); #else return S_ISDIR(ctx->s.st_mode); #endif @@ -2302,6 +2400,8 @@ static int ScpFileIsFile(ScpSendCtx* ctx) { #ifdef WOLFSSL_NUCLEUS return (ctx->s.fattribute != ADIRENT); +#elif defined(USE_WINDOWS_API) + return ((ctx->s.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0); #else return S_ISREG(ctx->s.st_mode); #endif @@ -2332,8 +2432,15 @@ static int ScpProcessEntry(WOLFSSH* ssh, char* fileName, word64* mTime, if (ret == WS_SUCCESS) { dirNameLen = (int)WSTRLEN(sendCtx->dirName); - #ifdef WOLFSSL_NUCLEUS - dNameLen = (int)WSTRLEN(sendCtx->currentDir->dir.lfname); + #if defined(WOLFSSL_NUCLEUS) + dNameLen = (int)WSTRLEN(sendCtx->currentDir->dir.lfname); + #elif defined(USE_WINDOWS_API) + { + char path[MAX_PATH]; + + GetFullPathNameA(fileName, MAX_PATH, path, NULL); + dNameLen = (int)WSTRLEN(path); + } #else dNameLen = (int)WSTRLEN(sendCtx->entry->d_name); #endif @@ -2354,6 +2461,11 @@ static int ScpProcessEntry(WOLFSSH* ssh, char* fileName, word64* mTime, if (wolfSSH_CleanPath(ssh, filePath) < 0) { ret = WS_SCP_ABORT; } + #elif defined(USE_WINDOWS_API) + WSTRNCAT(filePath, sendCtx->entry, + DEFAULT_SCP_FILE_NAME_SZ); + WSTRNCPY(fileName, sendCtx->entry, + DEFAULT_SCP_FILE_NAME_SZ); #else WSTRNCAT(filePath, sendCtx->entry->d_name, DEFAULT_SCP_FILE_NAME_SZ); @@ -2361,7 +2473,7 @@ static int ScpProcessEntry(WOLFSSH* ssh, char* fileName, word64* mTime, DEFAULT_SCP_FILE_NAME_SZ); #endif if (ret == WS_SUCCESS) { - ret = GetFileStats(sendCtx, filePath, mTime, aTime, fileMode); + ret = GetFileStats(ssh->fs, sendCtx, filePath, mTime, aTime, fileMode); } } } @@ -2370,7 +2482,7 @@ static int ScpProcessEntry(WOLFSSH* ssh, char* fileName, word64* mTime, if (ScpFileIsDir(sendCtx)) { - ret = ScpPushDir(sendCtx, filePath, ssh->ctx->heap); + ret = ScpPushDir(ssh->fs, sendCtx, filePath, ssh->ctx->heap); if (ret == WS_SUCCESS) { ret = WS_SCP_ENTER_DIR; } else { @@ -2379,7 +2491,7 @@ static int ScpProcessEntry(WOLFSSH* ssh, char* fileName, word64* mTime, } } else if (ScpFileIsFile(sendCtx)) { - if (WFOPEN(&(sendCtx->fp), filePath, "rb") != 0) { + if (WFOPEN(ssh->fs, &(sendCtx->fp), filePath, "rb") != 0) { WLOG(WS_LOG_ERROR, "scp: Error with oepning file, abort"); wolfSSH_SetScpErrorMsg(ssh, "unable to open file " "for reading"); @@ -2387,16 +2499,16 @@ static int ScpProcessEntry(WOLFSSH* ssh, char* fileName, word64* mTime, } if (ret == WS_SUCCESS) { - ret = GetFileSize(sendCtx->fp, totalFileSz); + ret = _GetFileSize(ssh->fs, sendCtx->fp, totalFileSz); if (ret == WS_SUCCESS) - ret = (word32)WFREAD(buf, 1, bufSz, sendCtx->fp); + ret = (word32)WFREAD(ssh->fs, buf, 1, bufSz, sendCtx->fp); } /* keep fp open if no errors and transfer will continue */ if ((sendCtx->fp != NULL) && ((ret < 0) || (*totalFileSz == (word32)ret))) { - WFCLOSE(sendCtx->fp); + WFCLOSE(ssh->fs, sendCtx->fp); } } @@ -2519,7 +2631,7 @@ int wsScpSendCallback(WOLFSSH* ssh, int state, const char* peerRequest, break; case WOLFSSH_SCP_SINGLE_FILE_REQUEST: - if ((sendCtx == NULL) || WFOPEN(&(sendCtx->fp), peerRequest, + if ((sendCtx == NULL) || WFOPEN(ssh->fs, &(sendCtx->fp), peerRequest, "rb") != 0) { WLOG(WS_LOG_ERROR, "scp: unable to open file, abort"); @@ -2538,10 +2650,10 @@ int wsScpSendCallback(WOLFSSH* ssh, int state, const char* peerRequest, } if (ret == WS_SUCCESS) - ret = GetFileSize(sendCtx->fp, totalFileSz); + ret = _GetFileSize(ssh->fs, sendCtx->fp, totalFileSz); if (ret == WS_SUCCESS) - ret = GetFileStats(sendCtx, peerRequest, mTime, aTime, fileMode); + ret = GetFileStats(ssh->fs, sendCtx, peerRequest, mTime, aTime, fileMode); if (ret == WS_SUCCESS) ret = ExtractFileName(peerRequest, fileName, fileNameSz); @@ -2549,7 +2661,7 @@ int wsScpSendCallback(WOLFSSH* ssh, int state, const char* peerRequest, if (ret == WS_SUCCESS && sendCtx != NULL && sendCtx->fp != NULL) { /* If it is an empty file, do not read. */ if (*totalFileSz != 0) { - ret = (word32)WFREAD(buf, 1, bufSz, sendCtx->fp); + ret = (word32)WFREAD(ssh->fs, buf, 1, bufSz, sendCtx->fp); if (ret == 0) { /* handle unexpected case */ ret = WS_EOF; } @@ -2562,7 +2674,7 @@ int wsScpSendCallback(WOLFSSH* ssh, int state, const char* peerRequest, /* keep fp open if no errors and transfer will continue */ if ((sendCtx != NULL) && (sendCtx->fp != NULL) && ((ret < 0) || (*totalFileSz == (word32)ret))) { - WFCLOSE(sendCtx->fp); + WFCLOSE(ssh->fs, sendCtx->fp); } break; @@ -2572,7 +2684,7 @@ int wsScpSendCallback(WOLFSSH* ssh, int state, const char* peerRequest, if (ScpDirStackIsEmpty(sendCtx)) { /* first request, keep track of request directory */ - ret = ScpPushDir(sendCtx, peerRequest, ssh->ctx->heap); + ret = ScpPushDir(ssh->fs, sendCtx, peerRequest, ssh->ctx->heap); if (ret == WS_SUCCESS) { /* get file name from request */ @@ -2580,7 +2692,7 @@ int wsScpSendCallback(WOLFSSH* ssh, int state, const char* peerRequest, } if (ret == WS_SUCCESS) { - ret = GetFileStats(sendCtx, peerRequest, mTime, aTime, + ret = GetFileStats(ssh->fs, sendCtx, peerRequest, mTime, aTime, fileMode); } @@ -2595,7 +2707,7 @@ int wsScpSendCallback(WOLFSSH* ssh, int state, const char* peerRequest, /* send directory msg or abort */ break; } - ret = FindNextDirEntry(sendCtx); + ret = FindNextDirEntry(ssh->fs, sendCtx); /* help out static analysis tool */ if (ret != WS_BAD_ARGUMENT && sendCtx == NULL) @@ -2609,7 +2721,7 @@ int wsScpSendCallback(WOLFSSH* ssh, int state, const char* peerRequest, /* reached end of directory */ if (sendCtx->entry == NULL) { #endif - ret = ScpPopDir(sendCtx, ssh->ctx->heap); + ret = ScpPopDir(ssh->fs, sendCtx, ssh->ctx->heap); if (ret == WS_SUCCESS) { ret = WS_SCP_EXIT_DIR; @@ -2645,13 +2757,13 @@ int wsScpSendCallback(WOLFSSH* ssh, int state, const char* peerRequest, break; } - ret = (word32)WFREAD(buf, 1, bufSz, sendCtx->fp); + ret = (word32)WFREAD(ssh->fs, buf, 1, bufSz, sendCtx->fp); if (ret == 0) { /* handle case of EOF */ ret = WS_EOF; } if ((ret <= 0) || (fileOffset + ret == *totalFileSz)) { - WFCLOSE(sendCtx->fp); + WFCLOSE(ssh->fs, sendCtx->fp); } break; diff --git a/src/wolfsftp.c b/src/wolfsftp.c index 5aa24c7fc..388188f6d 100644 --- a/src/wolfsftp.c +++ b/src/wolfsftp.c @@ -63,6 +63,7 @@ enum WS_SFTP_STATE_ID { STATE_ID_RECV = 0x10000, STATE_ID_CHMOD = 0x20000, STATE_ID_SETATR = 0x40000, + STATE_ID_RECV_INIT = 0x80000, }; enum WS_SFTP_CHMOD_STATE_ID { @@ -239,6 +240,11 @@ typedef struct WS_SFTP_LS_STATE { } WS_SFTP_LS_STATE; +typedef struct WS_SFTP_RECV_INIT_STATE { + WS_SFTP_BUFFER buffer; + word32 extSz; +} WS_SFTP_RECV_INIT_STATE; + enum WS_SFTP_GET_STATE_ID { STATE_GET_INIT, STATE_GET_LSTAT, @@ -762,6 +768,14 @@ static void wolfSSH_SFTP_ClearState(WOLFSSH* ssh, enum WS_SFTP_STATE_ID state) ssh->setatrState = NULL; } } + + if (state & STATE_ID_RECV_INIT) { + if (ssh->recvInitState) { + wolfSSH_SFTP_buffer_free(ssh, &ssh->recvInitState->buffer); + WFREE(ssh->recvInitState, ssh->ctx->heap, DYNTYPE_SFTP_STATE); + ssh->recvInitState = NULL; + } + } } } @@ -987,49 +1001,85 @@ static int SFTP_GetAttributes_Handle(WOLFSSH* ssh, byte* handle, int handleSz, * returns WS_SUCCESS on success */ static int SFTP_ServerRecvInit(WOLFSSH* ssh) { - int len; + enum { + RECV_INIT_SIZE = LENGTH_SZ + MSG_ID_SZ + UINT32_SZ + }; + + int ret; byte id; word32 sz = 0; word32 version = 0; - byte buf[LENGTH_SZ + MSG_ID_SZ + UINT32_SZ]; + WS_SFTP_RECV_INIT_STATE *state; - if ((len = wolfSSH_stream_read(ssh, buf, sizeof(buf))) != sizeof(buf)) { - return len; + state = ssh->recvInitState; + if (state == NULL) { + state = (WS_SFTP_RECV_INIT_STATE*)WMALLOC(sizeof(WS_SFTP_RECV_INIT_STATE), + ssh->ctx->heap, DYNTYPE_SFTP_STATE); + if (state == NULL) { + ssh->error = WS_MEMORY_E; + return WS_FATAL_ERROR; + } + WMEMSET(state, 0, sizeof(WS_SFTP_RECV_INIT_STATE)); + ssh->recvInitState = state; } - if (SFTP_GetSz(buf, &sz, - MSG_ID_SZ + UINT32_SZ, WOLFSSH_MAX_SFTP_RECV) != WS_SUCCESS) { - wolfSSH_SFTP_ClearState(ssh, STATE_ID_ALL); - return WS_BUFFER_E; - } + switch (ssh->sftpState) { + case SFTP_BEGIN: + ret = wolfSSH_SFTP_buffer_read(ssh, &state->buffer, RECV_INIT_SIZE); + if (ret < 0) { + return WS_FATAL_ERROR; + } - /* compare versions supported */ - id = buf[LENGTH_SZ]; - if (id != WOLFSSH_FTP_INIT) { - WLOG(WS_LOG_SFTP, "Unexpected SFTP type received"); - wolfSSH_SFTP_ClearState(ssh, STATE_ID_ALL); - return WS_BUFFER_E; - } + if (ret < WOLFSSH_SFTP_HEADER) { + WLOG(WS_LOG_SFTP, "Unable to read SFTP INIT message"); + return WS_FATAL_ERROR; + } - ato32(buf + LENGTH_SZ + MSG_ID_SZ, &version); - /* versions greater than WOLFSSH_SFTP_VERSION should fall back to ours - * versions less than WOLFSSH_SFTP_VERSION we should bail out on or - * implement a fall back */ - if (version < WOLFSSH_SFTP_VERSION) { - WLOG(WS_LOG_SFTP, "Unsupported SFTP version, sending version 3"); - wolfSSH_SFTP_ClearState(ssh, STATE_ID_ALL); - return WS_VERSION_E; - } + if (SFTP_GetSz(state->buffer.data, &sz, + MSG_ID_SZ + UINT32_SZ, WOLFSSH_MAX_SFTP_RECV) != WS_SUCCESS) { + wolfSSH_SFTP_ClearState(ssh, STATE_ID_ALL); + return WS_BUFFER_E; + } - /* silently ignore extensions if not supported */ - sz = sz - MSG_ID_SZ - UINT32_SZ; - if (sz > 0) { - byte* data = (byte*)WMALLOC(sz, NULL, DYNTYPE_BUFFER); - if (data == NULL) return WS_MEMORY_E; - len = wolfSSH_stream_read(ssh, data, sz); - WFREE(data, NULL, DYNTYPE_BUFFER); - if (len != (int)sz) - return len; + /* compare versions supported */ + id = state->buffer.data[LENGTH_SZ]; + if (id != WOLFSSH_FTP_INIT) { + WLOG(WS_LOG_SFTP, "Unexpected SFTP type received"); + wolfSSH_SFTP_ClearState(ssh, STATE_ID_ALL); + return WS_BUFFER_E; + } + + ato32(state->buffer.data + LENGTH_SZ + MSG_ID_SZ, &version); + /* versions greater than WOLFSSH_SFTP_VERSION should fall back to ours + * versions less than WOLFSSH_SFTP_VERSION we should bail out on or + * implement a fall back */ + if (version < WOLFSSH_SFTP_VERSION) { + WLOG(WS_LOG_SFTP, "Unsupported SFTP version, sending version 3"); + wolfSSH_SFTP_ClearState(ssh, STATE_ID_ALL); + return WS_VERSION_E; + } + + wolfSSH_SFTP_buffer_free(ssh, &state->buffer); + + state->extSz = sz - MSG_ID_SZ - UINT32_SZ; + ssh->sftpState = SFTP_EXT; + NO_BREAK; + + case SFTP_EXT: + /* silently ignore extensions if not supported */ + if (state->extSz > 0) { + ret = wolfSSH_SFTP_buffer_read(ssh, &state->buffer, (int)state->extSz); + if (ret < 0) { + return WS_FATAL_ERROR; + } + + if (ret < (int)state->extSz) { + WLOG(WS_LOG_SFTP, "Unable to read SFTP INIT extensions"); + return WS_FATAL_ERROR; + } + + wolfSSH_SFTP_buffer_free(ssh, &state->buffer); + } } ssh->reqId++; @@ -1063,7 +1113,7 @@ static int SFTP_ServerSendInit(WOLFSSH* ssh) { */ int wolfSSH_SFTP_accept(WOLFSSH* ssh) { - int ret = WS_SFTP_COMPLETE; + int ret = WS_FATAL_ERROR; if (ssh == NULL) { return WS_BAD_ARGUMENT; @@ -1090,18 +1140,25 @@ int wolfSSH_SFTP_accept(WOLFSSH* ssh) switch (ssh->sftpState) { case SFTP_BEGIN: - if (SFTP_ServerRecvInit(ssh) != WS_SUCCESS) { - return WS_FATAL_ERROR; + case SFTP_EXT: + ret = SFTP_ServerRecvInit(ssh); + if (ret != WS_SUCCESS) { + if (ssh->error != WS_WANT_READ && ssh->error != WS_WANT_WRITE) + wolfSSH_SFTP_ClearState(ssh, STATE_ID_ALL); + return ret; } ssh->sftpState = SFTP_RECV; NO_BREAK; case SFTP_RECV: - if (SFTP_ServerSendInit(ssh) != WS_SUCCESS) { - return WS_FATAL_ERROR; + ret = SFTP_ServerSendInit(ssh); + if (ret != WS_SUCCESS) { + return ret; } ssh->sftpState = SFTP_DONE; WLOG(WS_LOG_SFTP, "SFTP connection established"); + wolfSSH_SFTP_ClearState(ssh, STATE_ID_RECV_INIT); + ret = WS_SFTP_COMPLETE; break; default: @@ -1645,6 +1702,11 @@ int wolfSSH_SFTP_RecvRMDIR(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_RMDIR"); + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + ato32(data + idx, &sz); idx += UINT32_SZ; if (sz > maxSz - idx) { return WS_BUFFER_E; @@ -1720,6 +1782,11 @@ int wolfSSH_SFTP_RecvMKDIR(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_MKDIR"); + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + ato32(data + idx, &sz); idx += UINT32_SZ; if (sz > maxSz - idx) { return WS_BUFFER_E; @@ -1919,6 +1986,11 @@ int wolfSSH_SFTP_RecvOpen(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) return WS_FATAL_ERROR; } + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + ato32(data + idx, &sz); idx += UINT32_SZ; if (sz > maxSz - idx) { return WS_BUFFER_E; @@ -1966,7 +2038,7 @@ int wolfSSH_SFTP_RecvOpen(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) atr.per = 0644; } - fd = WOPEN(dir, m, atr.per); + fd = WOPEN(ssh->fs, dir, m, atr.per); if (fd < 0) { WLOG(WS_LOG_SFTP, "Error opening file %s", dir); res = oer; @@ -2047,6 +2119,11 @@ int wolfSSH_SFTP_RecvOpen(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) return WS_FATAL_ERROR; } + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + ato32(data + idx, &sz); idx += UINT32_SZ; if (sz > maxSz - idx) { return WS_BUFFER_E; @@ -2147,16 +2224,13 @@ int wolfSSH_SFTP_RecvOpen(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) #ifndef NO_WOLFSSH_DIR /* hold pointers to directory handles */ -typedef struct DIR_HANDLE { +struct WS_DIR_LIST { WDIR dir; char* dirName; /* base name of directory */ byte isEof; /* flag for if read everything */ word32 id[2]; /* handle ID */ - struct DIR_HANDLE* next; -} DIR_HANDLE; -static DIR_HANDLE* dirList = NULL; -static word32 idCount[2] = {0, 0}; -/* @TODO add locking for thread safety */ + struct WS_DIR_LIST* next; +}; /* Handles packet to open a directory @@ -2188,6 +2262,11 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) return WS_FATAL_ERROR; } + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + /* get directory name */ ato32(data + idx, &sz); idx += UINT32_SZ; if (sz > maxSz - idx) { @@ -2211,23 +2290,22 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) WOLFSSH_UNUSED(reqId); - /* add to directory list @TODO locking for thread safety */ if (ret == WS_SUCCESS) { - DIR_HANDLE* cur = NULL; + WS_DIR_LIST* cur = NULL; char* dirName = NULL; word32 dirNameSz; - cur = (DIR_HANDLE*)WMALLOC(sizeof(DIR_HANDLE), + cur = (WS_DIR_LIST*)WMALLOC(sizeof(WS_DIR_LIST), ssh->ctx->heap, DYNTYPE_SFTP); if (cur == NULL) { - WCLOSEDIR(&ctx); + WCLOSEDIR(ssh->fs, &ctx); return WS_MEMORY_E; } dirNameSz = (word32)WSTRLEN(dir) + 1; dirName = (char*)WMALLOC(dirNameSz, ssh->ctx->heap, DYNTYPE_PATH); if (dirName == NULL) { - WCLOSEDIR(&ctx); + WCLOSEDIR(ssh->fs, &ctx); WFREE(cur, ssh->ctx->heap, DYNTYPE_SFTP); return WS_MEMORY_E; } @@ -2237,15 +2315,15 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) #else cur->dir = ctx; #endif - cur->id[0] = id[0] = idCount[0]; - cur->id[1] = id[1] = idCount[1]; + cur->id[0] = id[0] = ssh->dirIdCount[0]; + cur->id[1] = id[1] = ssh->dirIdCount[1]; c32toa(id[0], idFlat); c32toa(id[1], idFlat + UINT32_SZ); - AddAssign64(idCount, 1); + AddAssign64(ssh->dirIdCount, 1); cur->isEof = 0; - cur->next = dirList; - dirList = cur; - dirList->dirName = dirName; /* take over ownership of buffer */ + cur->next = ssh->dirList; + ssh->dirList = cur; + ssh->dirList->dirName = dirName; /* take over ownership of buffer */ } out = (byte*)WMALLOC(outSz, ssh->ctx->heap, DYNTYPE_BUFFER); @@ -2298,6 +2376,11 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) return WS_FATAL_ERROR; } + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + /* get directory name */ ato32(data + idx, &sz); idx += UINT32_SZ; @@ -2364,24 +2447,23 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) WOLFSSH_UNUSED(reqId); - /* add to directory list @TODO locking for thread safety */ if (ret == WS_SUCCESS) { - DIR_HANDLE* cur = (DIR_HANDLE*)WMALLOC(sizeof(DIR_HANDLE), + WS_DIR_LIST* cur = (WS_DIR_LIST*)WMALLOC(sizeof(WS_DIR_LIST), ssh->ctx->heap, DYNTYPE_SFTP); if (cur == NULL) { WFREE(dirName, ssh->ctx->heap, DYNTYPE_BUFFER); return WS_MEMORY_E; } cur->dir = INVALID_HANDLE_VALUE; - cur->id[0] = id[0] = idCount[0]; - cur->id[1] = id[1] = idCount[1]; + cur->id[0] = id[0] = ssh->dirIdCount[0]; + cur->id[1] = id[1] = ssh->dirIdCount[1]; c32toa(id[0], idFlat); c32toa(id[1], idFlat + UINT32_SZ); - AddAssign64(idCount, 1); + AddAssign64(ssh->dirIdCount, 1); cur->isEof = 0; cur->dirName = dirName; /* take over ownership of buffer */ - cur->next = dirList; - dirList = cur; + cur->next = ssh->dirList; + ssh->dirList = cur; } @@ -2453,15 +2535,15 @@ static void getDate(char* buf, int len, struct tm* t) * return WS_SUCCESS on success */ static int SFTP_CreateLongName(WS_SFTPNAME* name) { +#if defined(XGMTIME) && defined(XSNPRINTF) + char sizeStr[32]; char perm[11]; int linkCount = 1; /* @TODO set to correct value */ -#if defined(XGMTIME) && defined(XSNPRINTF) char date[WS_DATE_SIZE + 1]; /* +1 for null terminator */ struct tm* localTime = NULL; -#endif - WS_SFTP_FILEATRB* atr; int i; - + WS_SFTP_FILEATRB* atr; +#endif int totalSz = 0; if (name == NULL) { @@ -2506,7 +2588,9 @@ static int SFTP_CreateLongName(WS_SFTPNAME* name) totalSz += name->fSz; /* size of file name */ totalSz += 7; /* for all ' ' spaces */ - totalSz += 3 + 8 + 8 + 8; /* linkCount + uid + gid + size */ + totalSz += 3 + 8 + 8; /* linkCount + uid + gid */ + WSNPRINTF(sizeStr, sizeof(sizeStr) - 1, "%8lld", ((long long int)atr->sz[1] << 32) + (long long int)(atr->sz[0])); + totalSz += WSTRLEN(sizeStr); #else totalSz = name->fSz; #endif @@ -2520,8 +2604,8 @@ static int SFTP_CreateLongName(WS_SFTPNAME* name) name->lName[totalSz] = '\0'; #if defined(XGMTIME) && defined(XSNPRINTF) - WSNPRINTF(name->lName, totalSz, "%s %3d %8d %8d %8d %s %s", - perm, linkCount, atr->uid, atr->gid, atr->sz[0], date, name->fName); + WSNPRINTF(name->lName, totalSz, "%s %3d %8d %8d %s %s %s", + perm, linkCount, atr->uid, atr->gid, sizeStr, date, name->fName); #else WMEMCPY(name->lName, name->fName, totalSz); #endif @@ -2618,7 +2702,7 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out, } } - if (!special && (WREADDIR(dir)) == NULL) { + if (!special && (WREADDIR(ssh->fs, dir)) == NULL) { ret = WS_NEXT_ERROR; } @@ -2892,7 +2976,7 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out, return WS_BAD_ARGUMENT; } - dp = WREADDIR(dir); + dp = WREADDIR(ssh->fs, dir); if (dp == NULL) { return WS_FATAL_ERROR; } @@ -3033,7 +3117,7 @@ int wolfSSH_SFTP_RecvReadDir(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) WS_SFTPNAME* name = NULL; WS_SFTPNAME* list = NULL; word32 outSz = 0; - DIR_HANDLE* cur = dirList; + WS_DIR_LIST* cur; char* dirName = NULL; byte* out; @@ -3043,10 +3127,16 @@ int wolfSSH_SFTP_RecvReadDir(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_READDIR"); + cur = ssh->dirList; #ifdef USE_WINDOWS_API dir = INVALID_HANDLE_VALUE; #endif + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + /* get directory handle */ ato32(data + idx, &sz); idx += UINT32_SZ; if (sz + idx > maxSz || sz > WOLFSSH_MAX_HANDLE) { @@ -3143,7 +3233,7 @@ int wolfSSH_SFTP_RecvReadDir(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) */ int wolfSSH_SFTP_RecvCloseDir(WOLFSSH* ssh, byte* handle, word32 handleSz) { - DIR_HANDLE* cur = dirList; + WS_DIR_LIST* cur; word32 h[2] = {0,0}; if (ssh == NULL || handle == NULL || handleSz != (sizeof(word32)*2)) { @@ -3153,6 +3243,7 @@ int wolfSSH_SFTP_RecvCloseDir(WOLFSSH* ssh, byte* handle, word32 handleSz) WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_CLOSE Directory"); /* find DIR given handle */ + cur = ssh->dirList; ato32(handle, &h[0]); ato32(handle + UINT32_SZ, &h[1]); while (cur != NULL) { @@ -3169,18 +3260,18 @@ int wolfSSH_SFTP_RecvCloseDir(WOLFSSH* ssh, byte* handle, word32 handleSz) #ifdef USE_WINDOWS_API FindClose(cur->dir); #else - WCLOSEDIR(&cur->dir); + WCLOSEDIR(ssh->fs, &cur->dir); #endif /* remove directory from list */ if (cur != NULL) { - DIR_HANDLE* pre = dirList; + WS_DIR_LIST* pre = ssh->dirList; WLOG(WS_LOG_SFTP, "Free'ing and closing handle %d%d pointer of [%p]", cur->id[1], cur->id[0], cur); /* case where node is at head of list */ if (pre == cur) { - dirList = cur->next; + ssh->dirList = cur->next; WFREE(cur->dirName, ssh->ctx->heap, DYNTYPE_SFTP); WFREE(cur, ssh->ctx->heap, DYNTYPE_SFTP); } @@ -3229,6 +3320,11 @@ int wolfSSH_SFTP_RecvWrite(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_WRITE"); + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + /* get file handle */ ato32(data + idx, &sz); idx += UINT32_SZ; if (sz + idx > maxSz || sz > WOLFSSH_MAX_HANDLE) { @@ -3252,7 +3348,7 @@ int wolfSSH_SFTP_RecvWrite(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) return WS_BUFFER_E; } - ret = WPWRITE(fd, data + idx, sz, ofst); + ret = WPWRITE(ssh->fs, fd, data + idx, sz, ofst); if (ret < 0) { #if defined(WOLFSSL_NUCLEUS) && defined(DEBUG_WOLFSSH) if (ret == NUF_NOSPC) { @@ -3313,6 +3409,11 @@ int wolfSSH_SFTP_RecvWrite(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_WRITE"); + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + /* get file handle */ ato32(data + idx, &sz); idx += UINT32_SZ; @@ -3404,6 +3505,11 @@ int wolfSSH_SFTP_RecvRead(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_READ"); + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + /* get file handle */ ato32(data + idx, &sz); idx += UINT32_SZ; if (sz + idx > maxSz || sz > WOLFSSH_MAX_HANDLE) { @@ -3429,7 +3535,7 @@ int wolfSSH_SFTP_RecvRead(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) return WS_MEMORY_E; } - ret = WPREAD(fd, out + UINT32_SZ + WOLFSSH_SFTP_HEADER, sz, ofst); + ret = WPREAD(ssh->fs, fd, out + UINT32_SZ + WOLFSSH_SFTP_HEADER, sz, ofst); if (ret < 0 || (word32)ret > sz) { WLOG(WS_LOG_SFTP, "Error reading from file"); res = err; @@ -3499,6 +3605,11 @@ int wolfSSH_SFTP_RecvRead(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_READ"); + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + /* get file handle */ ato32(data + idx, &sz); idx += UINT32_SZ; if (sz > maxSz - idx || sz > WOLFSSH_MAX_HANDLE) { @@ -3616,6 +3727,11 @@ int wolfSSH_SFTP_RecvClose(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_CLOSE"); + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + /* get file handle */ ato32(data + idx, &sz); idx += UINT32_SZ; if (sz + idx > maxSz || sz > WOLFSSH_MAX_HANDLE) { @@ -3632,7 +3748,7 @@ int wolfSSH_SFTP_RecvClose(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) if (sz == sizeof(WFD)) { WMEMSET((byte*)&fd, 0, sizeof(WFD)); WMEMCPY((byte*)&fd, data + idx, sz); - ret = WCLOSE(fd); + ret = WCLOSE(ssh->fs, fd); #ifdef WOLFSSH_STOREHANDLE if (SFTP_RemoveHandleNode(ssh, data + idx, sz) != WS_SUCCESS) { WLOG(WS_LOG_SFTP, "Unable to remove handle from list"); @@ -3697,6 +3813,11 @@ int wolfSSH_SFTP_RecvClose(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_CLOSE"); + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + /* get file handle */ ato32(data + idx, &sz); idx += UINT32_SZ; if (sz + idx > maxSz || sz > WOLFSSH_MAX_HANDLE) { @@ -3787,6 +3908,11 @@ int wolfSSH_SFTP_RecvRemove(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_REMOVE"); + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + /* get file name */ ato32(data + idx, &sz); idx += UINT32_SZ; if (sz + idx > maxSz || sz > WOLFSSH_MAX_HANDLE) { @@ -3873,6 +3999,11 @@ int wolfSSH_SFTP_RecvRename(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_RENAME"); + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + /* get old file name */ ato32(data + idx, &sz); idx += UINT32_SZ; if (sz > maxSz - idx) { @@ -4090,10 +4221,10 @@ int SFTP_GetAttributes(void* fs, const char* fileName, WS_SFTP_FILEATRB* atr, WOLFSSH_UNUSED(fs); if (noFollow) { - ret = WLSTAT(fileName, &stats); + ret = WLSTAT(ssh->fs, fileName, &stats); } else { - ret = WSTAT(fileName, &stats); + ret = WSTAT(ssh->fs, fileName, &stats); } WMEMSET(atr, 0, sizeof(WS_SFTP_FILEATRB)); @@ -4172,7 +4303,7 @@ int SFTP_GetAttributes_Handle(WOLFSSH* ssh, byte* handle, int handleSz, return WS_BAD_FILE_E; } - if (WSTAT(cur->name, &stats) != NU_SUCCESS) { + if (WSTAT(ssh->fs, cur->name, &stats) != NU_SUCCESS) { return WS_FATAL_ERROR; } @@ -4512,12 +4643,12 @@ int SFTP_GetAttributes(void* fs, const char* fileName, WS_SFTP_FILEATRB* atr, if (noFollow) { /* Note, for windows, we treat WSTAT and WLSTAT the same. */ - if (WLSTAT(fileName, &stats) != 0) { + if (WLSTAT(ssh->fs, fileName, &stats) != 0) { return WS_BAD_FILE_E; } } else { - if (WSTAT(fileName, &stats) != 0) { + if (WSTAT(ssh->fs, fileName, &stats) != 0) { return WS_BAD_FILE_E; } } @@ -4615,6 +4746,11 @@ int wolfSSH_SFTP_RecvFSTAT(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_FSTAT"); + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + ato32(data + idx, &handleSz); idx += UINT32_SZ; if (handleSz + idx > maxSz) { return WS_BUFFER_E; @@ -4683,6 +4819,11 @@ int wolfSSH_SFTP_RecvSTAT(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_STAT"); + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + ato32(data + idx, &sz); idx += UINT32_SZ; if (sz > maxSz - idx) { return WS_BUFFER_E; @@ -4762,6 +4903,11 @@ int wolfSSH_SFTP_RecvLSTAT(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_LSTAT"); + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + ato32(data + idx, &sz); idx += UINT32_SZ; if (sz > maxSz - idx) { return WS_BUFFER_E; @@ -4946,6 +5092,11 @@ int wolfSSH_SFTP_RecvSetSTAT(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz) WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_SETSTAT"); + if (maxSz < UINT32_SZ) { + /* not enough for an ato32 call */ + return WS_BUFFER_E; + } + ato32(data + idx, &sz); idx += UINT32_SZ; if (sz > maxSz - idx) { return WS_BUFFER_E; @@ -8221,9 +8372,9 @@ int wolfSSH_SFTP_Get(WOLFSSH* ssh, char* from, WLOG(WS_LOG_SFTP, "SFTP GET STATE: OPEN LOCAL"); #ifndef USE_WINDOWS_API if (state->gOfst[0] > 0 || state->gOfst[1] > 0) - ret = WFOPEN(&state->fl, to, "ab"); + ret = WFOPEN(ssh->fs, &state->fl, to, "ab"); else - ret = WFOPEN(&state->fl, to, "wb"); + ret = WFOPEN(ssh->fs, &state->fl, to, "wb"); #else /* USE_WINDOWS_API */ { DWORD desiredAccess = GENERIC_WRITE; @@ -8268,7 +8419,7 @@ int wolfSSH_SFTP_Get(WOLFSSH* ssh, char* from, } else { #ifndef USE_WINDOWS_API - if ((long)WFWRITE(state->r, 1, + if ((long)WFWRITE(ssh->fs, state->r, 1, sz, state->fl) != sz) { WLOG(WS_LOG_SFTP, "Error writing to file"); ssh->error = WS_BAD_FILE_E; @@ -8330,7 +8481,7 @@ int wolfSSH_SFTP_Get(WOLFSSH* ssh, char* from, case STATE_GET_CLOSE_LOCAL: WLOG(WS_LOG_SFTP, "SFTP GET STATE: CLOSE LOCAL"); #ifndef USE_WINDOWS_API - WFCLOSE(state->fl); + WFCLOSE(ssh->fs, state->fl); #else /* USE_WINDOWS_API */ if (CloseHandle(state->fileHandle) == 0) { WLOG(WS_LOG_SFTP, "Error closing file."); @@ -8419,7 +8570,7 @@ int wolfSSH_SFTP_Put(WOLFSSH* ssh, char* from, char* to, byte resume, case STATE_PUT_OPEN_LOCAL: WLOG(WS_LOG_SFTP, "SFTP PUT STATE: OPEN LOCAL"); #ifndef USE_WINDOWS_API - ret = WFOPEN(&state->fl, from, "rb"); + ret = WFOPEN(ssh->fs, &state->fl, from, "rb"); if (ret != 0) { WLOG(WS_LOG_SFTP, "Unable to open input file"); ssh->error = WS_SFTP_FILE_DNE; @@ -8433,7 +8584,7 @@ int wolfSSH_SFTP_Put(WOLFSSH* ssh, char* from, char* to, byte resume, #if SIZEOF_OFF_T == 8 offset = (((word64)state->pOfst[1]) << 32) | offset; #endif - WFSEEK(state->fl, offset, 0); + WFSEEK(ssh->fs, state->fl, offset, 0); } #else /* USE_WINDOWS_API */ state->fileHandle = WS_CreateFileA(from, GENERIC_READ, @@ -8447,7 +8598,7 @@ int wolfSSH_SFTP_Put(WOLFSSH* ssh, char* from, char* to, byte resume, } if (resume) { WMEMSET(&state->offset, 0, sizeof(OVERLAPPED)); - state->offset.OffsetHigh = 0; + state->offset.OffsetHigh = state->pOfst[1]; state->offset.Offset = state->pOfst[0]; } #endif /* USE_WINDOWS_API */ @@ -8478,7 +8629,7 @@ int wolfSSH_SFTP_Put(WOLFSSH* ssh, char* from, char* to, byte resume, do { if (state->rSz == 0) { #ifndef USE_WINDOWS_API - state->rSz = (int)WFREAD(state->r, + state->rSz = (int)WFREAD(ssh->fs, state->r, 1, WOLFSSH_MAX_SFTP_RW, state->fl); if (state->rSz <= 0) { break; /* either at end of file or error */ @@ -8503,7 +8654,7 @@ int wolfSSH_SFTP_Put(WOLFSSH* ssh, char* from, char* to, byte resume, else { AddAssign64(state->pOfst, sz); #ifdef USE_WINDOWS_API - state->offset.OffsetHigh = 0; + state->offset.OffsetHigh = state->pOfst[1]; state->offset.Offset = state->pOfst[0]; #endif /* USE_WINDOWS_API */ state->rSz -= sz; @@ -8522,7 +8673,7 @@ int wolfSSH_SFTP_Put(WOLFSSH* ssh, char* from, char* to, byte resume, case STATE_PUT_CLOSE_LOCAL: WLOG(WS_LOG_SFTP, "SFTP PUT STATE: CLOSE LOCAL"); #ifndef USE_WINDOWS_API - WFCLOSE(state->fl); + WFCLOSE(ssh->fs, state->fl); #else /* USE_WINDOWS_API */ CloseHandle(state->fileHandle); #endif /* USE_WINDOWS_API */ @@ -8573,7 +8724,7 @@ int wolfSSH_SFTP_free(WOLFSSH* ssh) /* go through and free handles and make sure files are closed */ while (cur != NULL) { - WCLOSE(*((WFD*)cur->handle)); + WCLOSE(ssh->fs, *((WFD*)cur->handle)); if (SFTP_RemoveHandleNode(ssh, cur->handle, cur->handleSz) != WS_SUCCESS) { return WS_FATAL_ERROR; @@ -8586,23 +8737,23 @@ int wolfSSH_SFTP_free(WOLFSSH* ssh) #ifndef NO_WOLFSSH_DIR { /* free all dirs if hung up on */ - DIR_HANDLE* cur = dirList; + WS_DIR_LIST* cur = ssh->dirList; /* find DIR given handle */ while (cur != NULL) { - DIR_HANDLE* toFree = cur; + WS_DIR_LIST* toFree = cur; cur = cur->next; #ifdef USE_WINDOWS_API FindClose(toFree->dir); #else - WCLOSEDIR(&toFree->dir); + WCLOSEDIR(ssh->fs, &toFree->dir); #endif if (toFree->dirName != NULL) WFREE(toFree->dirName, ssh->ctx->heap, DYNTYPE_SFTP); WFREE(toFree, ssh->ctx->heap, DYNTYPE_SFTP); } - dirList = NULL; + ssh->dirList = NULL; } #endif /* NO_WOLFSSH_DIR */ diff --git a/src/wolfterm.c b/src/wolfterm.c index 1bcea8f19..659256838 100644 --- a/src/wolfterm.c +++ b/src/wolfterm.c @@ -582,6 +582,11 @@ static int wolfSSH_DoControlSeq(WOLFSSH* ssh, WOLFSSH_HANDLE handle, byte* buf, i += 1; /* for 'h' or 'l' */ break; + case 'X': + /* @TODO (ECH) Erase number of characters on current line */ + break; + + default: WLOG(WS_LOG_DEBUG, "Unknown control sequence char:%c", c); i = *idx; diff --git a/wolfssh/internal.h b/wolfssh/internal.h index b96a43362..f0d7b63f6 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -493,6 +493,7 @@ struct WOLFSSH_CTX { word32 publicKeyAlgoCount; word32 highwaterMark; const char* banner; + const char* sshProtoIdStr; word32 bannerSz; word32 windowSz; word32 maxPacketSz; @@ -571,6 +572,9 @@ typedef struct HandshakeInfo { #ifdef WOLFSSH_SFTP #define WOLFSSH_MAX_SFTPOFST 3 +#ifndef NO_WOLFSSH_DIR + typedef struct WS_DIR_LIST WS_DIR_LIST; +#endif typedef struct WS_HANDLE_LIST WS_HANDLE_LIST; typedef struct SFTP_OFST { word32 offset[2]; @@ -578,6 +582,7 @@ typedef struct SFTP_OFST { char to[WOLFSSH_MAX_FILENAME]; } SFTP_OFST; +struct WS_SFTP_RECV_INIT_STATE; struct WS_SFTP_GET_STATE; struct WS_SFTP_PUT_STATE; struct WS_SFTP_LSTAT_STATE; @@ -751,9 +756,14 @@ struct WOLFSSH { word32 sftpExtSz; /* size of extension buffer (buffer not currently used) */ SFTP_OFST sftpOfst[WOLFSSH_MAX_SFTPOFST]; char* sftpDefaultPath; +#ifndef NO_WOLFSSH_DIR + WS_DIR_LIST* dirList; + word32 dirIdCount[2]; +#endif #ifdef WOLFSSH_STOREHANDLE WS_HANDLE_LIST* handleList; #endif + struct WS_SFTP_RECV_INIT_STATE* recvInitState; struct WS_SFTP_RECV_STATE* recvState; struct WS_SFTP_RMDIR_STATE* rmdirState; struct WS_SFTP_MKDIR_STATE* mkdirState; @@ -789,6 +799,12 @@ struct WOLFSSH { #ifdef WOLFSSH_FWD void* fwdCbCtx; #endif /* WOLFSSH_FWD */ +#ifdef WOLFSSH_TERM + WS_CallbackTerminalSize termResizeCb; + void* termCtx; + word32 curX; /* current terminal width */ + word32 curY; /* current terminal height */ +#endif }; @@ -867,6 +883,13 @@ WOLFSSH_LOCAL int wsEmbedSend(WOLFSSH*, void*, word32, void*); #endif /* WOLFSSH_USER_IO */ +enum ChannelOpenFailReasons { + OPEN_OK = 0, + OPEN_ADMINISTRATIVELY_PROHIBITED, + OPEN_CONNECT_FAILED, + OPEN_UNKNOWN_CHANNEL_TYPE, + OPEN_RESOURCE_SHORTAGE +}; WOLFSSH_LOCAL int DoReceive(WOLFSSH*); WOLFSSH_LOCAL int DoProtoId(WOLFSSH*); @@ -897,6 +920,7 @@ WOLFSSH_LOCAL int SendRequestSuccess(WOLFSSH*, int); WOLFSSH_LOCAL int SendChannelOpenSession(WOLFSSH*, WOLFSSH_CHANNEL*); WOLFSSH_LOCAL int SendChannelOpenForward(WOLFSSH*, WOLFSSH_CHANNEL*); WOLFSSH_LOCAL int SendChannelOpenConf(WOLFSSH*, WOLFSSH_CHANNEL*); +WOLFSSH_LOCAL int SendChannelOpenFail(WOLFSSH*, word32, word32, const char*, const char*); WOLFSSH_LOCAL int SendChannelEof(WOLFSSH*, word32); WOLFSSH_LOCAL int SendChannelEow(WOLFSSH*, word32); WOLFSSH_LOCAL int SendChannelClose(WOLFSSH*, word32); @@ -904,6 +928,8 @@ WOLFSSH_LOCAL int SendChannelExit(WOLFSSH*, word32, int); WOLFSSH_LOCAL int SendChannelData(WOLFSSH*, word32, byte*, word32); WOLFSSH_LOCAL int SendChannelWindowAdjust(WOLFSSH*, word32, word32); WOLFSSH_LOCAL int SendChannelRequest(WOLFSSH*, byte*, word32); +WOLFSSH_LOCAL int SendChannelTerminalResize(WOLFSSH*, word32, word32, word32, + word32); WOLFSSH_LOCAL int SendChannelTerminalRequest(WOLFSSH* ssh); WOLFSSH_LOCAL int SendChannelAgentRequest(WOLFSSH* ssh); WOLFSSH_LOCAL int SendChannelSuccess(WOLFSSH*, word32, int); diff --git a/wolfssh/port.h b/wolfssh/port.h index 0603cee1f..5b96e0622 100644 --- a/wolfssh/port.h +++ b/wolfssh/port.h @@ -48,10 +48,16 @@ extern "C" { #else #define WISSPACE isspace #endif +#ifdef USE_WINDOWS_API + #define WUID_T int + #define WGID_T int + #define WPASSWD struct passwd +#else #define WUID_T uid_t #define WGID_T gid_t #define WPASSWD struct passwd #endif +#endif /* setup memory handling */ #ifndef WMALLOC_USER @@ -99,14 +105,14 @@ extern "C" { #define WFILE int WOLFSSH_API int wfopen(WFILE**, const char*, const char*); - #define WFOPEN(f,fn,m) wfopen((f),(fn),(m)) - #define WFCLOSE(f) NU_Close(*(f)) - #define WFWRITE(b,x,s,f) ((s) != 0)? NU_Write(*(f),(const CHAR*)(b),(s)): 0 - #define WFREAD(b,x,s,f) NU_Read(*(f),(CHAR*)(b),(s)) - #define WFSEEK(s,o,w) NU_Seek(*(s),(o),(w)) - #define WFTELL(s) NU_Seek(*(s), 0, PSEEK_CUR) - #define WREWIND(s) NU_Seek(*(s), 0, PSEEK_SET) - #define WSEEK_END PSEEK_END + #define WFOPEN(fs, f,fn,m) wfopen((f),(fn),(m)) + #define WFCLOSE(fs,f) NU_Close(*(f)) + #define WFWRITE(fs,b,x,s,f) ((s) != 0)? NU_Write(*(f),(const CHAR*)(b),(s)): 0 + #define WFREAD(fs,b,x,s,f) NU_Read(*(f),(CHAR*)(b),(s)) + #define WFSEEK(fs,s,o,w) NU_Seek(*(s),(o),(w)) + #define WFTELL(fs,s) NU_Seek(*(s), 0, PSEEK_CUR) + #define WREWIND(fs,s) NU_Seek(*(s), 0, PSEEK_SET) + #define WSEEK_END PSEEK_END #define WS_DELIM '\\' #define WOLFSSH_O_RDWR PO_RDWR @@ -124,10 +130,10 @@ extern "C" { return NU_Open(f, PO_TEXT | flag, (PS_IWRITE | PS_IREAD)); } - #define WOPEN(f,m,p) wOpen((f),(m),(p)) + #define WOPEN(fs,f,m,p) wOpen((f),(m),(p)) #endif - #define WCLOSE(fd) NU_Close((fd)) + #define WCLOSE(fs,fd) NU_Close((fd)) static inline int wChmod(const char* f, int mode) { unsigned char atr = 0; @@ -176,13 +182,13 @@ extern "C" { WOLFSSH_API int wfopen(WFILE**, const char*, const char*); - #define WFOPEN(f,fn,m) wfopen((f),(fn),(m)) - #define WFCLOSE(f) fclose((f)) - #define WFREAD(b,s,a,f) fread((b),(s),(a),(f)) - #define WFWRITE(b,x,s,f) fwrite((b),(x),(s),(f)) - #define WFSEEK(s,o,w) fseek((s),(o),(w)) - #define WFTELL(s) ftell((s)) - #define WREWIND(s) fseek((s), 0, IO_SEEK_SET) + #define WFOPEN(fs,f,fn,m) wfopen((f),(fn),(m)) + #define WFCLOSE(fs,f) fclose((f)) + #define WFREAD(fs,b,s,a,f) fread((b),(s),(a),(f)) + #define WFWRITE(fs,b,x,s,f) fwrite((b),(x),(s),(f)) + #define WFSEEK(fs,s,o,w) fseek((s),(o),(w)) + #define WFTELL(fs,s) ftell((s)) + #define WREWIND(fs,s) fseek((s), 0, IO_SEEK_SET) #define WSEEK_END IO_SEEK_END #define WBADFILE NULL @@ -275,7 +281,7 @@ extern "C" { ret = f_open(*f, filename, m); return (int)ret; } - #define WFOPEN(f, fn, m) ff_fopen((f),(fn),(m)) + #define WFOPEN(fs, f, fn, m) ff_fopen((f),(fn),(m)) #ifdef WOLFSSH_XILFATFS static inline int ff_close(WFILE *f) @@ -287,7 +293,7 @@ extern "C" { } #endif - #define WFCLOSE(f) f_close(f) + #define WFCLOSE(fs,f) f_close(f) static inline int ff_read(void *ptr, size_t size, size_t nmemb, WFILE *f) { @@ -301,7 +307,7 @@ extern "C" { } return (n_bytes / size); } - #define WFREAD(b,s,a,f) ff_read(b,s,a,f) + #define WFREAD(fs,b,s,a,f) ff_read(b,s,a,f) static inline int ff_write(const void *ptr, size_t size, size_t nmemb, WFILE *f) { FRESULT ret; @@ -314,9 +320,9 @@ extern "C" { } return (n_bytes / size); } - #define WFWRITE(b,s,a,f) ff_write(b,s,a,f) + #define WFWRITE(fs,b,s,a,f) ff_write(b,s,a,f) - #define WFTELL(s) f_tell((s)) + #define WFTELL(fs,s) f_tell((s)) static inline int ff_seek(WFILE *fp, long off, int whence) { switch(whence) { @@ -333,17 +339,17 @@ extern "C" { } return -1; } - #define WFSEEK(s,o,w) ff_seek((s),(o),(w)) + #define WFSEEK(fs,s,o,w) ff_seek((s),(o),(w)) #ifdef WOLFSSH_XILFATFS - #define WREWIND(s) ff_seek(s, WSEEK_SET, 0) + #define WREWIND(fs,s) ff_seek(s, WSEEK_SET, 0) #else - #define WREWIND(s) f_rewind(s) + #define WREWIND(fs,s) f_rewind(s) #endif #define WBADFILE (-1) #ifndef NO_WOLFSSH_DIR #define WDIR DIR #define WOPENDIR(fs,h,c,d) f_opendir((c),(d)) - #define WCLOSEDIR(d) f_closedir(d) + #define WCLOSEDIR(fs,d) f_closedir(d) #endif #elif defined(WOLFSSH_USER_FILESYSTEM) /* User-defined I/O support */ @@ -355,20 +361,21 @@ extern "C" { #define WFILE FILE WOLFSSH_API int wfopen(WFILE**, const char*, const char*); - #define WFOPEN(f,fn,m) wfopen((f),(fn),(m)) - #define WFCLOSE(f) fclose(f) - #define WFREAD(b,s,a,f) fread((b),(s),(a),(f)) - #define WFWRITE(b,s,a,f) fwrite((b),(s),(a),(f)) - #define WFSEEK(s,o,w) fseek((s),(o),(w)) - #define WFTELL(s) ftell((s)) - #define WREWIND(s) rewind((s)) - #define WSEEK_END SEEK_END - #define WBADFILE NULL + #define WFOPEN(fs,f,fn,m) wfopen((f),(fn),(m)) + #define WFCLOSE(fs,f) fclose(f) + #define WFREAD(fs,b,s,a,f) fread((b),(s),(a),(f)) + #define WFWRITE(fs,b,s,a,f) fwrite((b),(s),(a),(f)) + #define WFSEEK(fs,s,o,w) fseek((s),(o),(w)) + #define WFTELL(fs,s) ftell((s)) + #define WREWIND(fs,s) rewind((s)) + #define WSEEK_END SEEK_END + #define WBADFILE NULL #define WSETTIME(fs,f,a,m) (0) #define WFSETTIME(fs,fd,a,m) (0) - #ifdef WOLFSSL_VXWORKS #define WUTIMES(f,t) (WS_SUCCESS) + #elif defined(USE_WINDOWS_API) + #include #else #define WUTIMES(f,t) utimes((f),(t)) #endif @@ -408,13 +415,13 @@ extern "C" { #ifndef _WIN32_WCE #include - #define WCHDIR(p) _chdir((p)) + #define WCHDIR(fs,p) _chdir((p)) #define WMKDIR(fs,p,m) _mkdir((p)) #endif #else #include #include - #define WCHDIR(p) chdir((p)) + #define WCHDIR(fs,p) chdir((p)) #ifdef WOLFSSL_VXWORKS #define WMKDIR(fs,p,m) mkdir((p)) #else @@ -456,7 +463,7 @@ extern "C" { #ifdef USE_WINDOWS_API #define WSTRNCPY(s1,s2,n) strncpy_s((s1),(n),(s2),(n)) #define WSTRNCASECMP(s1,s2,n) _strnicmp((s1),(s2),(n)) - #define WSNPRINTF(s,n,f,...) _snprintf_s((s),(n),(n),(f),##__VA_ARGS__) + #define WSNPRINTF(s,n,f,...) _snprintf_s((s),(n),_TRUNCATE,(f),##__VA_ARGS__) #define WVSNPRINTF(s,n,f,...) _vsnprintf_s((s),(n),(n),(f),##__VA_ARGS__) #define WSTRTOK(s1,s2,s3) strtok_s((s1),(s2),(s3)) #elif defined(MICROCHIP_MPLAB_HARMONY) || defined(MICROCHIP_PIC32) @@ -486,7 +493,7 @@ extern "C" { #endif /* WSTRING_USER */ /* get local time for debug print out */ -#ifdef USE_WINDOWS_API +#if defined(USE_WINDOWS_API) || defined(__MINGW32__) #define WTIME time #define WLOCALTIME(c,r) (localtime_s((r),(c))==0) #elif defined(MICROCHIP_MPLAB_HARMONY) @@ -534,8 +541,8 @@ extern "C" { #define WSTAT_T struct stat #define WRMDIR(fs,d) (NU_Remove_Dir((d)) == NU_SUCCESS)?0:1 #define WMKDIR(fs,d,m) (NU_Make_Dir((d)) == NU_SUCCESS)?0:1 - #define WSTAT(p,b) NU_Get_First((b),(p)) - #define WLSTAT(p,b) NU_Get_First((b),(p)) + #define WSTAT(fs,p,b) NU_Get_First((b),(p)) + #define WLSTAT(fs,p,b) NU_Get_First((b),(p)) #define WREMOVE(fs,d) NU_Delete((d)) #ifndef WS_MAX_RENAME_BUF @@ -558,39 +565,39 @@ extern "C" { WFILE* fNew; unsigned char buf[WS_MAX_RENAME_BUF]; - if ((ret = WFOPEN(&fOld, o, "rb")) != 0) { + if ((ret = WFOPEN(NULL, &fOld, o, "rb")) != 0) { return ret; } - if ((ret = WFOPEN(&fNew, n, "rwb")) != 0) { - WFCLOSE(fOld); + if ((ret = WFOPEN(NULL, &fNew, n, "rwb")) != 0) { + WFCLOSE(NULL, fOld); return ret; } /* read from the file in chunks and write chunks to new file */ do { - ret = WFREAD(buf, 1, WS_MAX_RENAME_BUF, fOld); + ret = WFREAD(NULL, buf, 1, WS_MAX_RENAME_BUF, fOld); if (ret > 0) { - if ((WFWRITE(buf, 1, ret, fNew)) != ret) { - WFCLOSE(fOld); - WFCLOSE(fNew); + if ((WFWRITE(NULL, buf, 1, ret, fNew)) != ret) { + WFCLOSE(NULL, fOld); + WFCLOSE(NULL, fNew); WREMOVE(NULL, n); return NUF_BADPARM; } } } while (ret > 0); - if (WFTELL(fOld) == WFSEEK(fOld, 0, WSEEK_END)) { + if (WFTELL(NULL, fOld) == WFSEEK(NULL, fOld, 0, WSEEK_END)) { /* wrote everything from file */ - WFCLOSE(fOld); + WFCLOSE(NULL, fOld); WREMOVE(NULL, o); - WFCLOSE(fNew); + WFCLOSE(NULL, fNew); } else { /* unable to write everything to file */ - WFCLOSE(fNew); + WFCLOSE(NULL, fNew); WREMOVE(NULL, n); - WFCLOSE(fOld); + WFCLOSE(NULL, fOld); return NUF_BADPARM; } @@ -639,7 +646,7 @@ extern "C" { return NU_Write(fd, (const CHAR*)buf, sz); } - #define WPWRITE(fd,b,s,o) wPwrite((fd),(b),(s),(o)) + #define WPWRITE(fs,fd,b,s,o) wPwrite((fd),(b),(s),(o)) #endif #ifndef WPREAD @@ -653,7 +660,7 @@ extern "C" { return NU_Read(fd, (CHAR*)buf, sz); } - #define WPREAD(fd,b,s,o) wPread((fd),(b),(s),(o)) + #define WPREAD(fs,fd,b,s,o) wPread((fd),(b),(s),(o)) #endif static inline int wUtimes(const char* f, struct timeval t[2]) @@ -743,8 +750,8 @@ extern "C" { #define WOPENDIR(fs,h,c,d) wOpenDir((c),(d)) #endif - #define WCLOSEDIR(d) NU_Done((d)) - #define WREADDIR(d) (NU_Get_Next((d)) == NU_SUCCESS)?(d):NULL + #define WCLOSEDIR(fs,d) NU_Done((d)) + #define WREADDIR(fs,d) (NU_Get_Next((d)) == NU_SUCCESS)?(d):NULL #endif /* NO_WOLFSSH_DIR */ #elif defined(FREESCALE_MQX) @@ -806,7 +813,7 @@ extern "C" { return fopen(f, mode); } - #define WOPEN(f,m,p) wOpen((f),(m),(p)) + #define WOPEN(fs,f,m,p) wOpen((f),(m),(p)) #endif #ifndef WRMDIR @@ -985,7 +992,7 @@ extern "C" { #define WGETCWD(fs,r,rSz) wGetCwd((fs),(r),(rSz)) #endif /* WGETCWD */ - #define WCLOSE fclose + #define WCLOSE(fs,fd) fclose(fd) #ifndef WPWRITE static inline int wPwrite(WFD fd, unsigned char* buf, unsigned int sz, @@ -1000,7 +1007,7 @@ extern "C" { return fwrite(buf, sz, 1, fd); } - #define WPWRITE(fd,b,s,o) wPwrite((fd),(b),(s),(o)) + #define WPWRITE(fs,fd,b,s,o) wPwrite((fd),(b),(s),(o)) #endif #ifndef WPREAD @@ -1016,7 +1023,7 @@ extern "C" { return fread(buf, 1, sz, fd); } - #define WPREAD(fd,b,s,o) wPread((fd),(b),(s),(o)) + #define WPREAD(fs,fd,b,s,o) wPread((fd),(b),(s),(o)) #endif #ifndef NO_WOLFSSH_DIR @@ -1114,7 +1121,7 @@ extern "C" { return 0; } - #define WCLOSEDIR(d) wCloseDir((d)) + #define WCLOSEDIR(fs,d) wCloseDir((d)) #endif /* WCLOSEDIR */ #endif /* NO_WOLFSSH_DIR */ @@ -1122,10 +1129,10 @@ extern "C" { #define WSTAT_T FILINFO #define WRMDIR(fs, d) f_unlink((d)) - #define WSTAT(p,b) f_stat(p,b) - #define WLSTAT(p,b) f_stat(p,b) + #define WSTAT(fs,p,b) f_stat(p,b) + #define WLSTAT(fs,p,b) f_stat(p,b) #define WREMOVE(fs,d) f_unlink((d)) - #define WRENAME(fd,o,n) f_rename((o),(n)) + #define WRENAME(fs,fd,o,n) f_rename((o),(n)) #define WMKDIR(fs, p, m) f_mkdir(p) #define WFD int @@ -1133,10 +1140,10 @@ extern "C" { int ff_close(int fd); int ff_pwrite(int fd, const byte *buffer, int sz); int ff_pread(int fd, byte *buffer, int sz); - #define WOPEN(f,m,p) ff_open(f,m,p) - #define WPWRITE(fd,b,s,o) ff_pwrite(fd,b,s) - #define WPREAD(fd,b,s,o) ff_pread(fd,b,s) - #define WCLOSE(fd) ff_close(fd) + #define WOPEN(fs,f,m,p) ff_open(f,m,p) + #define WPWRITE(fs,fd,b,s,o) ff_pwrite(fd,b,s) + #define WPREAD(fs,fd,b,s,o) ff_pread(fd,b,s) + #define WCLOSE(fs,fd) ff_close(fd) static inline int ff_chmod(const char *fname, int mode) { @@ -1187,19 +1194,19 @@ extern "C" { #define WSTAT_T struct _stat #define WRMDIR(fs,d) _rmdir((d)) - #define WSTAT(p,b) _stat((p),(b)) - #define WLSTAT(p,b) _stat((p),(b)) + #define WSTAT(fs,p,b) _stat((p),(b)) + #define WLSTAT(fs,p,b) _stat((p),(b)) #define WREMOVE(fs,d) remove((d)) #define WRENAME(fs,o,n) rename((o),(n)) #define WGETCWD(fs,r,rSz) _getcwd((r),(rSz)) - #define WOPEN(f,m,p) _open((f),(m),(p)) - #define WCLOSE(fd) _close((fd)) + #define WOPEN(fs,f,m,p) _open((f),(m),(p)) + #define WCLOSE(fs,fd) _close((fd)) #define WFD int int wPwrite(WFD, unsigned char*, unsigned int, const unsigned int*); int wPread(WFD, unsigned char*, unsigned int, const unsigned int*); - #define WPWRITE(fd,b,s,o) wPwrite((fd),(b),(s),(o)) - #define WPREAD(fd,b,s,o) wPread((fd),(b),(s),(o)) + #define WPWRITE(fs,fd,b,s,o) wPwrite((fd),(b),(s),(o)) + #define WPREAD(fs,fd,b,s,o) wPread((fd),(b),(s),(o)) #define WS_DELIM '\\' #define WOLFSSH_O_RDWR _O_RDWR @@ -1226,11 +1233,11 @@ extern "C" { #define WSTAT_T struct stat #define WRMDIR(fs,d) rmdir((d)) - #define WSTAT(p,b) stat((p),(b)) + #define WSTAT(fs,p,b) stat((p),(b)) #ifndef USE_OSE_API - #define WLSTAT(p,b) lstat((p),(b)) + #define WLSTAT(fs,p,b) lstat((p),(b)) #else - #define WLSTAT(p,b) stat((p),(b)) + #define WLSTAT(fs,p,b) stat((p),(b)) #endif #define WREMOVE(fs,d) remove((d)) #define WRENAME(fs,o,n) rename((o),(n)) @@ -1247,12 +1254,12 @@ extern "C" { #define WOLFSSH_O_TRUNC O_TRUNC #define WOLFSSH_O_EXCL O_EXCL - #define WOPEN(f,m,p) open((f),(m),(p)) - #define WCLOSE(fd) close((fd)) + #define WOPEN(fs,f,m,p) open((f),(m),(p)) + #define WCLOSE(fs,fd) close((fd)) int wPwrite(WFD, unsigned char*, unsigned int, const unsigned int*); int wPread(WFD, unsigned char*, unsigned int, const unsigned int*); - #define WPWRITE(fd,b,s,o) wPwrite((fd),(b),(s),(o)) - #define WPREAD(fd,b,s,o) wPread((fd),(b),(s),(o)) + #define WPWRITE(fs,fd,b,s,o) wPwrite((fd),(b),(s),(o)) + #define WPREAD(fs,fd,b,s,o) wPread((fd),(b),(s),(o)) #ifndef NO_WOLFSSH_DIR #include /* used for opendir, readdir, and closedir */ @@ -1260,9 +1267,9 @@ extern "C" { /* returns 0 on success */ #define WOPENDIR(fs,h,c,d) ((*(c) = opendir((d))) == NULL) - #define WCLOSEDIR(d) closedir(*(d)) - #define WREADDIR(d) readdir(*(d)) - #define WREWINDDIR(d) rewinddir(*(d)) + #define WCLOSEDIR(fs,d) closedir(*(d)) + #define WREADDIR(fs,d) readdir(*(d)) + #define WREWINDDIR(fs,d) rewinddir(*(d)) #endif /* NO_WOLFSSH_DIR */ #endif #endif /* WOLFSSH_SFTP or WOLFSSH_SCP */ diff --git a/wolfssh/ssh.h b/wolfssh/ssh.h index a42004b37..cad91b7b3 100644 --- a/wolfssh/ssh.h +++ b/wolfssh/ssh.h @@ -236,6 +236,7 @@ WOLFSSH_API int wolfSSH_SetUsername(WOLFSSH*, const char*); WOLFSSH_API char* wolfSSH_GetUsername(WOLFSSH*); WOLFSSH_API int wolfSSH_CTX_SetBanner(WOLFSSH_CTX*, const char*); +WOLFSSH_API int wolfSSH_CTX_SetSshProtoIdStr(WOLFSSH_CTX*, const char*); WOLFSSH_API int wolfSSH_CTX_UsePrivateKey_buffer(WOLFSSH_CTX*, const byte*, word32, int); #ifdef WOLFSSH_CERTS @@ -282,6 +283,13 @@ typedef enum { WOLFSSH_API WS_SessionType wolfSSH_GetSessionType(const WOLFSSH*); WOLFSSH_API const char* wolfSSH_GetSessionCommand(const WOLFSSH*); WOLFSSH_API int wolfSSH_SetChannelType(WOLFSSH*, byte, byte*, word32); +WOLFSSH_API int wolfSSH_ChangeTerminalSize(WOLFSSH* ssh, word32, word32, + word32, word32); +typedef int (*WS_CallbackTerminalSize)(WOLFSSH*, word32, word32, word32, + word32, void*); +WOLFSSH_API void wolfSSH_SetTerminalResizeCb(WOLFSSH* ssh, + WS_CallbackTerminalSize cb); +WOLFSSH_API void wolfSSH_SetTerminalResizeCtx(WOLFSSH* ssh, void* usrCtx); enum WS_HighwaterSide { @@ -317,7 +325,8 @@ enum WS_UserAuthResults WOLFSSH_USERAUTH_INVALID_USER, WOLFSSH_USERAUTH_INVALID_PASSWORD, WOLFSSH_USERAUTH_REJECTED, - WOLFSSH_USERAUTH_INVALID_PUBLICKEY + WOLFSSH_USERAUTH_INVALID_PUBLICKEY, + WOLFSSH_USERAUTH_PARTIAL_SUCCESS }; enum WS_DisconnectReasonCodes { diff --git a/wolfssh/test.h b/wolfssh/test.h index 52062e9f0..eda5fe549 100644 --- a/wolfssh/test.h +++ b/wolfssh/test.h @@ -719,8 +719,8 @@ static INLINE int tcp_select(SOCKET_T socketfd, int to_sec) int depth, res; WFILE* file; for(depth = 0; depth <= MAX_WOLF_ROOT_DEPTH; depth++) { - if (WFOPEN(&file, serverKeyRsaPemFile, "rb") == 0) { - WFCLOSE(file); + if (WFOPEN(NULL, &file, serverKeyRsaPemFile, "rb") == 0) { + WFCLOSE(NULL, file); return depth; } #ifdef USE_WINDOWS_API diff --git a/wolfssh/visibility.h b/wolfssh/visibility.h index e0ba2591c..ae41a312b 100644 --- a/wolfssh/visibility.h +++ b/wolfssh/visibility.h @@ -41,7 +41,7 @@ extern "C" { */ #if defined(BUILDING_WOLFSSH) - #if defined(_MSC_VER) || defined(__CYGWIN__) + #if defined(_MSC_VER) || defined(__CYGWIN__) || defined(__MINGW32__) #ifdef WOLFSSH_DLL #define WOLFSSH_API extern __declspec(dllexport) #else @@ -59,7 +59,7 @@ extern "C" { #define WOLFSSH_LOCAL #endif /* HAVE_VISIBILITY */ #else /* BUILDING_WOLFSSH */ - #if defined(_MSC_VER) || defined(__CYGWIN__) + #if defined(_MSC_VER) || defined(__CYGWIN__) || defined(__MINGW32__) #ifdef WOLFSSH_DLL #define WOLFSSH_API extern __declspec(dllimport) #else diff --git a/wolfssh/wolfscp.h b/wolfssh/wolfscp.h index d944624db..de57895d9 100644 --- a/wolfssh/wolfscp.h +++ b/wolfssh/wolfscp.h @@ -76,13 +76,16 @@ enum WS_ScpFileStates { #include typedef struct ScpSendCtx { - #ifndef WOLFSSL_NUCLEUS - struct dirent* entry; /* file entry, from readdir() */ - struct stat s; /* stat info from file */ - #else + #ifdef WOLFSSL_NUCLEUS int fd; /* file descriptor, in the case of Nucleus fp points to fd */ DSTAT s; int nextError; + #elif defined(USE_WINDOWS_API) + char* entry; + WIN32_FILE_ATTRIBUTE_DATA s; + #else + struct dirent* entry; /* file entry, from readdir() */ + struct stat s; /* stat info from file */ #endif WFILE* fp; /* file pointer */ struct ScpDir* currentDir; /* dir being copied, stack */