in extensions/sftp/client/SFTPClient.cpp [247:447]
bool SFTPClient::connect() {
if (connected_) {
return true;
}
/* Setting up curl request */
std::stringstream uri_ss;
uri_ss << hostname_ << ":" << port_;
auto uri = uri_ss.str();
if (curl_easy_setopt(easy_, CURLOPT_URL, uri.c_str()) != CURLE_OK) {
return false;
}
if (curl_easy_setopt(easy_, CURLOPT_ERRORBUFFER, curl_errorbuffer_.data()) != CURLE_OK) {
return false;
}
if (curl_easy_setopt(easy_, CURLOPT_NOSIGNAL, 1L) != CURLE_OK) {
return false;
}
if (curl_easy_setopt(easy_, CURLOPT_CONNECT_ONLY, 1L) != CURLE_OK) {
return false;
}
/* Connecting to proxy, if needed, then to the host */
CURLcode curl_res = curl_easy_perform(easy_);
if (curl_res != CURLE_OK) {
logger_->log_error("Failed to connect to %s, curl error code: %s, detailed error message: %s",
uri.c_str(),
curl_easy_strerror(curl_res),
curl_errorbuffer_.data());
return false;
}
/* Getting socket from curl */
#ifdef WIN32
curl_socket_t sockfd;
/* Only CURLINFO_ACTIVESOCKET works on Win64 */
curl_res = curl_easy_getinfo(easy_, CURLINFO_ACTIVESOCKET, &sockfd);
#else
long sockfd; // NOLINT(runtime/int) long due to libcurl API
/* Some older cURL versions only support CURLINFO_LASTSOCKET */
curl_res = curl_easy_getinfo(easy_, CURLINFO_LASTSOCKET, &sockfd);
#endif
if (curl_res != CURLE_OK) {
return false;
}
/* Establishing SSH connection */
if (libssh2_session_handshake(ssh_session_, sockfd) != 0) {
char *err_msg = nullptr;
libssh2_session_last_error(ssh_session_, &err_msg, nullptr, 0);
logger_->log_info("Failed to establish SSH connection, error: %s", err_msg);
return false;
}
/* Checking remote host */
if (ssh_known_hosts_ != nullptr) {
auto hostkey_opt = [this]() -> std::optional<std::tuple<std::string_view, int>> {
size_t hostkey_len = 0U;
int type = LIBSSH2_HOSTKEY_TYPE_UNKNOWN;
const char *hostkey_ptr = libssh2_session_hostkey(ssh_session_, &hostkey_len, &type);
if (hostkey_ptr == nullptr) {
char *err_msg = nullptr;
libssh2_session_last_error(ssh_session_, &err_msg, nullptr, 0);
logger_->log_info("Failed to get session hostkey, error: %s", err_msg);
return std::nullopt;
}
const auto hostkey = std::string_view{hostkey_ptr, hostkey_len};
return std::make_optional(std::make_tuple(hostkey, type));
}();
if (!hostkey_opt) return false;
const auto [hostkey, hostkey_type] = *std::move(hostkey_opt);
int keybit = 0;
switch (hostkey_type) {
case LIBSSH2_HOSTKEY_TYPE_RSA:
keybit = LIBSSH2_KNOWNHOST_KEY_SSHRSA;
break;
case LIBSSH2_HOSTKEY_TYPE_DSS:
keybit = LIBSSH2_KNOWNHOST_KEY_SSHDSS;
break;
default:
logger_->log_error("Unknown host key type: %d", hostkey_type);
return false;
}
struct libssh2_knownhost* known_host = nullptr;
int keycheck_result = libssh2_knownhost_checkp(ssh_known_hosts_,
hostname_.c_str(),
-1 /*port*/,
hostkey.data(), hostkey.size(),
LIBSSH2_KNOWNHOST_TYPE_PLAIN |
LIBSSH2_KNOWNHOST_KEYENC_RAW |
keybit,
&known_host);
bool host_match = false;
switch (keycheck_result) {
case LIBSSH2_KNOWNHOST_CHECK_FAILURE:
logger_->log_warn("Failed to verify host key for %s", hostname_.c_str());
break;
case LIBSSH2_KNOWNHOST_CHECK_NOTFOUND:
logger_->log_warn("Host %s not found in the host key file", hostname_.c_str());
break;
case LIBSSH2_KNOWNHOST_CHECK_MISMATCH: {
auto hostkey_b64 = utils::StringUtils::to_base64(hostkey);
logger_->log_warn("Host key mismatch for %s, expected: %s, actual: %s", hostname_.c_str(),
known_host == nullptr ? "" : known_host->key, hostkey_b64.c_str());
break;
}
case LIBSSH2_KNOWNHOST_CHECK_MATCH:
host_match = true;
logger_->log_debug("Host key verification succeeded for %s", hostname_.c_str());
break;
default:
logger_->log_error("Unknown libssh2_knownhost_checkp result: %d", keycheck_result);
break;
}
if (strict_host_checking_ && !host_match) {
return false;
}
} else {
const char* fingerprint = libssh2_hostkey_hash(ssh_session_, LIBSSH2_HOSTKEY_HASH_SHA1);
const auto fingerprint_span = gsl::make_span(fingerprint, 20);
if (fingerprint == nullptr) {
logger_->log_warn("Cannot get remote server fingerprint");
} else {
auto fingerprint_hex = utils::StringUtils::to_hex(fingerprint_span.as_span<const std::byte>());
std::stringstream fingerprint_hex_colon;
for (size_t i = 0; i < 20; i++) {
fingerprint_hex_colon << fingerprint_hex.substr(i * 2, 2);
if (i != 19) {
fingerprint_hex_colon << ":";
}
}
logger_->log_debug("SHA1 host key fingerprint for %s is %s", hostname_.c_str(), fingerprint_hex_colon.str().c_str());
}
}
/* Getting possible authentication methods */
bool authenticated = false;
std::set<std::string> auth_methods;
char* userauthlist = libssh2_userauth_list(ssh_session_, username_.c_str(), strlen(username_.c_str()));
if (userauthlist == nullptr) {
if (libssh2_userauth_authenticated(ssh_session_) == 1) {
authenticated = true;
logger_->log_warn("SSH server authenticated with SSH_USERAUTH_NONE - this is unusual");
} else {
logger_->log_error("Failed to get supported SSH authentication methods");
return false;
}
} else {
auto methods_split = utils::StringUtils::split(userauthlist, ",");
auth_methods.insert(std::make_move_iterator(methods_split.begin()), std::make_move_iterator(methods_split.end()));
}
/* Authenticating */
if (!authenticated && public_key_authentication_enabled_ && auth_methods.count("publickey") == 1) {
if (libssh2_userauth_publickey_fromfile_ex(ssh_session_,
username_.c_str(),
username_.length(),
nullptr /*publickey*/,
private_key_file_path_.c_str(),
private_key_passphrase_.c_str()) == 0) {
authenticated = true;
logger_->log_debug("Successfully authenticated with publickey");
} else {
char *err_msg = nullptr;
libssh2_session_last_error(ssh_session_, &err_msg, nullptr, 0);
logger_->log_info("Failed to authenticate with publickey, error: %s", err_msg);
}
}
if (!authenticated && password_authentication_enabled_ && auth_methods.count("password") == 1) {
if (libssh2_userauth_password(ssh_session_, username_.c_str(), password_.c_str()) == 0) {
authenticated = true;
logger_->log_debug("Successfully authenticated with password");
} else {
char *err_msg = nullptr;
libssh2_session_last_error(ssh_session_, &err_msg, nullptr, 0);
logger_->log_info("Failed to authenticate with password, error: %s", err_msg);
}
}
if (!authenticated) {
logger_->log_error("Could not authenticate with any available method");
return false;
}
/* Initializing SFTP session */
sftp_session_ = libssh2_sftp_init(ssh_session_);
if (sftp_session_ == nullptr) {
char *err_msg = nullptr;
libssh2_session_last_error(ssh_session_, &err_msg, nullptr, 0);
logger_->log_error("Failed to initialize SFTP session, error: %s", err_msg);
return false;
}
connected_ = true;
/* Set up keepalive config if needed */
if (send_keepalive_) {
libssh2_keepalive_config(ssh_session_, 0 /*want_reply*/, 10U /*interval*/);
}
return true;
}