113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Red Hat, Inc. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * Authors: Nikos Mavrogiannopoulos, Tomas Mraz, Stanislav Zidek, 1113498266Sopenharmony_ci * Robert Kolcun, Andreas Schneider 1213498266Sopenharmony_ci * 1313498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1413498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1513498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1613498266Sopenharmony_ci * 1713498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1813498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1913498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 2213498266Sopenharmony_ci * KIND, either express or implied. 2313498266Sopenharmony_ci * 2413498266Sopenharmony_ci * SPDX-License-Identifier: curl 2513498266Sopenharmony_ci * 2613498266Sopenharmony_ci ***************************************************************************/ 2713498266Sopenharmony_ci 2813498266Sopenharmony_ci#include "curl_setup.h" 2913498266Sopenharmony_ci 3013498266Sopenharmony_ci#ifdef USE_LIBSSH 3113498266Sopenharmony_ci 3213498266Sopenharmony_ci#include <limits.h> 3313498266Sopenharmony_ci 3413498266Sopenharmony_ci/* in 0.10.0 or later, ignore deprecated warnings */ 3513498266Sopenharmony_ci#define SSH_SUPPRESS_DEPRECATED 3613498266Sopenharmony_ci#include <libssh/libssh.h> 3713498266Sopenharmony_ci#include <libssh/sftp.h> 3813498266Sopenharmony_ci 3913498266Sopenharmony_ci#ifdef HAVE_NETINET_IN_H 4013498266Sopenharmony_ci#include <netinet/in.h> 4113498266Sopenharmony_ci#endif 4213498266Sopenharmony_ci#ifdef HAVE_ARPA_INET_H 4313498266Sopenharmony_ci#include <arpa/inet.h> 4413498266Sopenharmony_ci#endif 4513498266Sopenharmony_ci#ifdef HAVE_NETDB_H 4613498266Sopenharmony_ci#include <netdb.h> 4713498266Sopenharmony_ci#endif 4813498266Sopenharmony_ci#ifdef __VMS 4913498266Sopenharmony_ci#include <in.h> 5013498266Sopenharmony_ci#include <inet.h> 5113498266Sopenharmony_ci#endif 5213498266Sopenharmony_ci 5313498266Sopenharmony_ci#include <curl/curl.h> 5413498266Sopenharmony_ci#include "urldata.h" 5513498266Sopenharmony_ci#include "sendf.h" 5613498266Sopenharmony_ci#include "hostip.h" 5713498266Sopenharmony_ci#include "progress.h" 5813498266Sopenharmony_ci#include "transfer.h" 5913498266Sopenharmony_ci#include "escape.h" 6013498266Sopenharmony_ci#include "http.h" /* for HTTP proxy tunnel stuff */ 6113498266Sopenharmony_ci#include "ssh.h" 6213498266Sopenharmony_ci#include "url.h" 6313498266Sopenharmony_ci#include "speedcheck.h" 6413498266Sopenharmony_ci#include "getinfo.h" 6513498266Sopenharmony_ci#include "strdup.h" 6613498266Sopenharmony_ci#include "strcase.h" 6713498266Sopenharmony_ci#include "vtls/vtls.h" 6813498266Sopenharmony_ci#include "cfilters.h" 6913498266Sopenharmony_ci#include "connect.h" 7013498266Sopenharmony_ci#include "inet_ntop.h" 7113498266Sopenharmony_ci#include "parsedate.h" /* for the week day and month names */ 7213498266Sopenharmony_ci#include "sockaddr.h" /* required for Curl_sockaddr_storage */ 7313498266Sopenharmony_ci#include "strtoofft.h" 7413498266Sopenharmony_ci#include "multiif.h" 7513498266Sopenharmony_ci#include "select.h" 7613498266Sopenharmony_ci#include "warnless.h" 7713498266Sopenharmony_ci#include "curl_path.h" 7813498266Sopenharmony_ci 7913498266Sopenharmony_ci#ifdef HAVE_SYS_STAT_H 8013498266Sopenharmony_ci#include <sys/stat.h> 8113498266Sopenharmony_ci#endif 8213498266Sopenharmony_ci#ifdef HAVE_UNISTD_H 8313498266Sopenharmony_ci#include <unistd.h> 8413498266Sopenharmony_ci#endif 8513498266Sopenharmony_ci#ifdef HAVE_FCNTL_H 8613498266Sopenharmony_ci#include <fcntl.h> 8713498266Sopenharmony_ci#endif 8813498266Sopenharmony_ci 8913498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 9013498266Sopenharmony_ci#include "curl_printf.h" 9113498266Sopenharmony_ci#include "curl_memory.h" 9213498266Sopenharmony_ci#include "memdebug.h" 9313498266Sopenharmony_ci 9413498266Sopenharmony_ci/* A recent macro provided by libssh. Or make our own. */ 9513498266Sopenharmony_ci#ifndef SSH_STRING_FREE_CHAR 9613498266Sopenharmony_ci#define SSH_STRING_FREE_CHAR(x) \ 9713498266Sopenharmony_ci do { \ 9813498266Sopenharmony_ci if(x) { \ 9913498266Sopenharmony_ci ssh_string_free_char(x); \ 10013498266Sopenharmony_ci x = NULL; \ 10113498266Sopenharmony_ci } \ 10213498266Sopenharmony_ci } while(0) 10313498266Sopenharmony_ci#endif 10413498266Sopenharmony_ci 10513498266Sopenharmony_ci/* These stat values may not be the same as the user's S_IFMT / S_IFLNK */ 10613498266Sopenharmony_ci#ifndef SSH_S_IFMT 10713498266Sopenharmony_ci#define SSH_S_IFMT 00170000 10813498266Sopenharmony_ci#endif 10913498266Sopenharmony_ci#ifndef SSH_S_IFLNK 11013498266Sopenharmony_ci#define SSH_S_IFLNK 0120000 11113498266Sopenharmony_ci#endif 11213498266Sopenharmony_ci 11313498266Sopenharmony_ci/* Local functions: */ 11413498266Sopenharmony_cistatic CURLcode myssh_connect(struct Curl_easy *data, bool *done); 11513498266Sopenharmony_cistatic CURLcode myssh_multi_statemach(struct Curl_easy *data, 11613498266Sopenharmony_ci bool *done); 11713498266Sopenharmony_cistatic CURLcode myssh_do_it(struct Curl_easy *data, bool *done); 11813498266Sopenharmony_ci 11913498266Sopenharmony_cistatic CURLcode scp_done(struct Curl_easy *data, 12013498266Sopenharmony_ci CURLcode, bool premature); 12113498266Sopenharmony_cistatic CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done); 12213498266Sopenharmony_cistatic CURLcode scp_disconnect(struct Curl_easy *data, 12313498266Sopenharmony_ci struct connectdata *conn, 12413498266Sopenharmony_ci bool dead_connection); 12513498266Sopenharmony_ci 12613498266Sopenharmony_cistatic CURLcode sftp_done(struct Curl_easy *data, 12713498266Sopenharmony_ci CURLcode, bool premature); 12813498266Sopenharmony_cistatic CURLcode sftp_doing(struct Curl_easy *data, 12913498266Sopenharmony_ci bool *dophase_done); 13013498266Sopenharmony_cistatic CURLcode sftp_disconnect(struct Curl_easy *data, 13113498266Sopenharmony_ci struct connectdata *conn, 13213498266Sopenharmony_ci bool dead); 13313498266Sopenharmony_cistatic 13413498266Sopenharmony_ciCURLcode sftp_perform(struct Curl_easy *data, 13513498266Sopenharmony_ci bool *connected, 13613498266Sopenharmony_ci bool *dophase_done); 13713498266Sopenharmony_ci 13813498266Sopenharmony_cistatic void sftp_quote(struct Curl_easy *data); 13913498266Sopenharmony_cistatic void sftp_quote_stat(struct Curl_easy *data); 14013498266Sopenharmony_cistatic int myssh_getsock(struct Curl_easy *data, 14113498266Sopenharmony_ci struct connectdata *conn, curl_socket_t *sock); 14213498266Sopenharmony_ci 14313498266Sopenharmony_cistatic CURLcode myssh_setup_connection(struct Curl_easy *data, 14413498266Sopenharmony_ci struct connectdata *conn); 14513498266Sopenharmony_ci 14613498266Sopenharmony_ci/* 14713498266Sopenharmony_ci * SCP protocol handler. 14813498266Sopenharmony_ci */ 14913498266Sopenharmony_ci 15013498266Sopenharmony_ciconst struct Curl_handler Curl_handler_scp = { 15113498266Sopenharmony_ci "SCP", /* scheme */ 15213498266Sopenharmony_ci myssh_setup_connection, /* setup_connection */ 15313498266Sopenharmony_ci myssh_do_it, /* do_it */ 15413498266Sopenharmony_ci scp_done, /* done */ 15513498266Sopenharmony_ci ZERO_NULL, /* do_more */ 15613498266Sopenharmony_ci myssh_connect, /* connect_it */ 15713498266Sopenharmony_ci myssh_multi_statemach, /* connecting */ 15813498266Sopenharmony_ci scp_doing, /* doing */ 15913498266Sopenharmony_ci myssh_getsock, /* proto_getsock */ 16013498266Sopenharmony_ci myssh_getsock, /* doing_getsock */ 16113498266Sopenharmony_ci ZERO_NULL, /* domore_getsock */ 16213498266Sopenharmony_ci myssh_getsock, /* perform_getsock */ 16313498266Sopenharmony_ci scp_disconnect, /* disconnect */ 16413498266Sopenharmony_ci ZERO_NULL, /* write_resp */ 16513498266Sopenharmony_ci ZERO_NULL, /* connection_check */ 16613498266Sopenharmony_ci ZERO_NULL, /* attach connection */ 16713498266Sopenharmony_ci PORT_SSH, /* defport */ 16813498266Sopenharmony_ci CURLPROTO_SCP, /* protocol */ 16913498266Sopenharmony_ci CURLPROTO_SCP, /* family */ 17013498266Sopenharmony_ci PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */ 17113498266Sopenharmony_ci}; 17213498266Sopenharmony_ci 17313498266Sopenharmony_ci/* 17413498266Sopenharmony_ci * SFTP protocol handler. 17513498266Sopenharmony_ci */ 17613498266Sopenharmony_ci 17713498266Sopenharmony_ciconst struct Curl_handler Curl_handler_sftp = { 17813498266Sopenharmony_ci "SFTP", /* scheme */ 17913498266Sopenharmony_ci myssh_setup_connection, /* setup_connection */ 18013498266Sopenharmony_ci myssh_do_it, /* do_it */ 18113498266Sopenharmony_ci sftp_done, /* done */ 18213498266Sopenharmony_ci ZERO_NULL, /* do_more */ 18313498266Sopenharmony_ci myssh_connect, /* connect_it */ 18413498266Sopenharmony_ci myssh_multi_statemach, /* connecting */ 18513498266Sopenharmony_ci sftp_doing, /* doing */ 18613498266Sopenharmony_ci myssh_getsock, /* proto_getsock */ 18713498266Sopenharmony_ci myssh_getsock, /* doing_getsock */ 18813498266Sopenharmony_ci ZERO_NULL, /* domore_getsock */ 18913498266Sopenharmony_ci myssh_getsock, /* perform_getsock */ 19013498266Sopenharmony_ci sftp_disconnect, /* disconnect */ 19113498266Sopenharmony_ci ZERO_NULL, /* write_resp */ 19213498266Sopenharmony_ci ZERO_NULL, /* connection_check */ 19313498266Sopenharmony_ci ZERO_NULL, /* attach connection */ 19413498266Sopenharmony_ci PORT_SSH, /* defport */ 19513498266Sopenharmony_ci CURLPROTO_SFTP, /* protocol */ 19613498266Sopenharmony_ci CURLPROTO_SFTP, /* family */ 19713498266Sopenharmony_ci PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION 19813498266Sopenharmony_ci | PROTOPT_NOURLQUERY /* flags */ 19913498266Sopenharmony_ci}; 20013498266Sopenharmony_ci 20113498266Sopenharmony_cistatic CURLcode sftp_error_to_CURLE(int err) 20213498266Sopenharmony_ci{ 20313498266Sopenharmony_ci switch(err) { 20413498266Sopenharmony_ci case SSH_FX_OK: 20513498266Sopenharmony_ci return CURLE_OK; 20613498266Sopenharmony_ci 20713498266Sopenharmony_ci case SSH_FX_NO_SUCH_FILE: 20813498266Sopenharmony_ci case SSH_FX_NO_SUCH_PATH: 20913498266Sopenharmony_ci return CURLE_REMOTE_FILE_NOT_FOUND; 21013498266Sopenharmony_ci 21113498266Sopenharmony_ci case SSH_FX_PERMISSION_DENIED: 21213498266Sopenharmony_ci case SSH_FX_WRITE_PROTECT: 21313498266Sopenharmony_ci return CURLE_REMOTE_ACCESS_DENIED; 21413498266Sopenharmony_ci 21513498266Sopenharmony_ci case SSH_FX_FILE_ALREADY_EXISTS: 21613498266Sopenharmony_ci return CURLE_REMOTE_FILE_EXISTS; 21713498266Sopenharmony_ci 21813498266Sopenharmony_ci default: 21913498266Sopenharmony_ci break; 22013498266Sopenharmony_ci } 22113498266Sopenharmony_ci 22213498266Sopenharmony_ci return CURLE_SSH; 22313498266Sopenharmony_ci} 22413498266Sopenharmony_ci 22513498266Sopenharmony_ci#ifndef DEBUGBUILD 22613498266Sopenharmony_ci#define state(x,y) mystate(x,y) 22713498266Sopenharmony_ci#else 22813498266Sopenharmony_ci#define state(x,y) mystate(x,y, __LINE__) 22913498266Sopenharmony_ci#endif 23013498266Sopenharmony_ci 23113498266Sopenharmony_ci/* 23213498266Sopenharmony_ci * SSH State machine related code 23313498266Sopenharmony_ci */ 23413498266Sopenharmony_ci/* This is the ONLY way to change SSH state! */ 23513498266Sopenharmony_cistatic void mystate(struct Curl_easy *data, sshstate nowstate 23613498266Sopenharmony_ci#ifdef DEBUGBUILD 23713498266Sopenharmony_ci , int lineno 23813498266Sopenharmony_ci#endif 23913498266Sopenharmony_ci ) 24013498266Sopenharmony_ci{ 24113498266Sopenharmony_ci struct connectdata *conn = data->conn; 24213498266Sopenharmony_ci struct ssh_conn *sshc = &conn->proto.sshc; 24313498266Sopenharmony_ci#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) 24413498266Sopenharmony_ci /* for debug purposes */ 24513498266Sopenharmony_ci static const char *const names[] = { 24613498266Sopenharmony_ci "SSH_STOP", 24713498266Sopenharmony_ci "SSH_INIT", 24813498266Sopenharmony_ci "SSH_S_STARTUP", 24913498266Sopenharmony_ci "SSH_HOSTKEY", 25013498266Sopenharmony_ci "SSH_AUTHLIST", 25113498266Sopenharmony_ci "SSH_AUTH_PKEY_INIT", 25213498266Sopenharmony_ci "SSH_AUTH_PKEY", 25313498266Sopenharmony_ci "SSH_AUTH_PASS_INIT", 25413498266Sopenharmony_ci "SSH_AUTH_PASS", 25513498266Sopenharmony_ci "SSH_AUTH_AGENT_INIT", 25613498266Sopenharmony_ci "SSH_AUTH_AGENT_LIST", 25713498266Sopenharmony_ci "SSH_AUTH_AGENT", 25813498266Sopenharmony_ci "SSH_AUTH_HOST_INIT", 25913498266Sopenharmony_ci "SSH_AUTH_HOST", 26013498266Sopenharmony_ci "SSH_AUTH_KEY_INIT", 26113498266Sopenharmony_ci "SSH_AUTH_KEY", 26213498266Sopenharmony_ci "SSH_AUTH_GSSAPI", 26313498266Sopenharmony_ci "SSH_AUTH_DONE", 26413498266Sopenharmony_ci "SSH_SFTP_INIT", 26513498266Sopenharmony_ci "SSH_SFTP_REALPATH", 26613498266Sopenharmony_ci "SSH_SFTP_QUOTE_INIT", 26713498266Sopenharmony_ci "SSH_SFTP_POSTQUOTE_INIT", 26813498266Sopenharmony_ci "SSH_SFTP_QUOTE", 26913498266Sopenharmony_ci "SSH_SFTP_NEXT_QUOTE", 27013498266Sopenharmony_ci "SSH_SFTP_QUOTE_STAT", 27113498266Sopenharmony_ci "SSH_SFTP_QUOTE_SETSTAT", 27213498266Sopenharmony_ci "SSH_SFTP_QUOTE_SYMLINK", 27313498266Sopenharmony_ci "SSH_SFTP_QUOTE_MKDIR", 27413498266Sopenharmony_ci "SSH_SFTP_QUOTE_RENAME", 27513498266Sopenharmony_ci "SSH_SFTP_QUOTE_RMDIR", 27613498266Sopenharmony_ci "SSH_SFTP_QUOTE_UNLINK", 27713498266Sopenharmony_ci "SSH_SFTP_QUOTE_STATVFS", 27813498266Sopenharmony_ci "SSH_SFTP_GETINFO", 27913498266Sopenharmony_ci "SSH_SFTP_FILETIME", 28013498266Sopenharmony_ci "SSH_SFTP_TRANS_INIT", 28113498266Sopenharmony_ci "SSH_SFTP_UPLOAD_INIT", 28213498266Sopenharmony_ci "SSH_SFTP_CREATE_DIRS_INIT", 28313498266Sopenharmony_ci "SSH_SFTP_CREATE_DIRS", 28413498266Sopenharmony_ci "SSH_SFTP_CREATE_DIRS_MKDIR", 28513498266Sopenharmony_ci "SSH_SFTP_READDIR_INIT", 28613498266Sopenharmony_ci "SSH_SFTP_READDIR", 28713498266Sopenharmony_ci "SSH_SFTP_READDIR_LINK", 28813498266Sopenharmony_ci "SSH_SFTP_READDIR_BOTTOM", 28913498266Sopenharmony_ci "SSH_SFTP_READDIR_DONE", 29013498266Sopenharmony_ci "SSH_SFTP_DOWNLOAD_INIT", 29113498266Sopenharmony_ci "SSH_SFTP_DOWNLOAD_STAT", 29213498266Sopenharmony_ci "SSH_SFTP_CLOSE", 29313498266Sopenharmony_ci "SSH_SFTP_SHUTDOWN", 29413498266Sopenharmony_ci "SSH_SCP_TRANS_INIT", 29513498266Sopenharmony_ci "SSH_SCP_UPLOAD_INIT", 29613498266Sopenharmony_ci "SSH_SCP_DOWNLOAD_INIT", 29713498266Sopenharmony_ci "SSH_SCP_DOWNLOAD", 29813498266Sopenharmony_ci "SSH_SCP_DONE", 29913498266Sopenharmony_ci "SSH_SCP_SEND_EOF", 30013498266Sopenharmony_ci "SSH_SCP_WAIT_EOF", 30113498266Sopenharmony_ci "SSH_SCP_WAIT_CLOSE", 30213498266Sopenharmony_ci "SSH_SCP_CHANNEL_FREE", 30313498266Sopenharmony_ci "SSH_SESSION_DISCONNECT", 30413498266Sopenharmony_ci "SSH_SESSION_FREE", 30513498266Sopenharmony_ci "QUIT" 30613498266Sopenharmony_ci }; 30713498266Sopenharmony_ci 30813498266Sopenharmony_ci 30913498266Sopenharmony_ci if(sshc->state != nowstate) { 31013498266Sopenharmony_ci infof(data, "SSH %p state change from %s to %s (line %d)", 31113498266Sopenharmony_ci (void *) sshc, names[sshc->state], names[nowstate], 31213498266Sopenharmony_ci lineno); 31313498266Sopenharmony_ci } 31413498266Sopenharmony_ci#endif 31513498266Sopenharmony_ci 31613498266Sopenharmony_ci sshc->state = nowstate; 31713498266Sopenharmony_ci} 31813498266Sopenharmony_ci 31913498266Sopenharmony_ci/* Multiple options: 32013498266Sopenharmony_ci * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5 32113498266Sopenharmony_ci * hash (90s style auth, not sure we should have it here) 32213498266Sopenharmony_ci * 2. data->set.ssh_keyfunc callback is set. Then we do trust on first 32313498266Sopenharmony_ci * use. We even save on knownhosts if CURLKHSTAT_FINE_ADD_TO_FILE 32413498266Sopenharmony_ci * is returned by it. 32513498266Sopenharmony_ci * 3. none of the above. We only accept if it is present on known hosts. 32613498266Sopenharmony_ci * 32713498266Sopenharmony_ci * Returns SSH_OK or SSH_ERROR. 32813498266Sopenharmony_ci */ 32913498266Sopenharmony_cistatic int myssh_is_known(struct Curl_easy *data) 33013498266Sopenharmony_ci{ 33113498266Sopenharmony_ci int rc; 33213498266Sopenharmony_ci struct connectdata *conn = data->conn; 33313498266Sopenharmony_ci struct ssh_conn *sshc = &conn->proto.sshc; 33413498266Sopenharmony_ci ssh_key pubkey; 33513498266Sopenharmony_ci size_t hlen; 33613498266Sopenharmony_ci unsigned char *hash = NULL; 33713498266Sopenharmony_ci char *found_base64 = NULL; 33813498266Sopenharmony_ci char *known_base64 = NULL; 33913498266Sopenharmony_ci int vstate; 34013498266Sopenharmony_ci enum curl_khmatch keymatch; 34113498266Sopenharmony_ci struct curl_khkey foundkey; 34213498266Sopenharmony_ci struct curl_khkey *knownkeyp = NULL; 34313498266Sopenharmony_ci curl_sshkeycallback func = 34413498266Sopenharmony_ci data->set.ssh_keyfunc; 34513498266Sopenharmony_ci 34613498266Sopenharmony_ci#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0) 34713498266Sopenharmony_ci struct ssh_knownhosts_entry *knownhostsentry = NULL; 34813498266Sopenharmony_ci struct curl_khkey knownkey; 34913498266Sopenharmony_ci#endif 35013498266Sopenharmony_ci 35113498266Sopenharmony_ci#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0) 35213498266Sopenharmony_ci rc = ssh_get_server_publickey(sshc->ssh_session, &pubkey); 35313498266Sopenharmony_ci#else 35413498266Sopenharmony_ci rc = ssh_get_publickey(sshc->ssh_session, &pubkey); 35513498266Sopenharmony_ci#endif 35613498266Sopenharmony_ci if(rc != SSH_OK) 35713498266Sopenharmony_ci return rc; 35813498266Sopenharmony_ci 35913498266Sopenharmony_ci if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) { 36013498266Sopenharmony_ci int i; 36113498266Sopenharmony_ci char md5buffer[33]; 36213498266Sopenharmony_ci const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]; 36313498266Sopenharmony_ci 36413498266Sopenharmony_ci rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5, 36513498266Sopenharmony_ci &hash, &hlen); 36613498266Sopenharmony_ci if(rc != SSH_OK || hlen != 16) { 36713498266Sopenharmony_ci failf(data, 36813498266Sopenharmony_ci "Denied establishing ssh session: md5 fingerprint not available"); 36913498266Sopenharmony_ci goto cleanup; 37013498266Sopenharmony_ci } 37113498266Sopenharmony_ci 37213498266Sopenharmony_ci for(i = 0; i < 16; i++) 37313498266Sopenharmony_ci msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char)hash[i]); 37413498266Sopenharmony_ci 37513498266Sopenharmony_ci infof(data, "SSH MD5 fingerprint: %s", md5buffer); 37613498266Sopenharmony_ci 37713498266Sopenharmony_ci if(!strcasecompare(md5buffer, pubkey_md5)) { 37813498266Sopenharmony_ci failf(data, 37913498266Sopenharmony_ci "Denied establishing ssh session: mismatch md5 fingerprint. " 38013498266Sopenharmony_ci "Remote %s is not equal to %s", md5buffer, pubkey_md5); 38113498266Sopenharmony_ci rc = SSH_ERROR; 38213498266Sopenharmony_ci goto cleanup; 38313498266Sopenharmony_ci } 38413498266Sopenharmony_ci 38513498266Sopenharmony_ci rc = SSH_OK; 38613498266Sopenharmony_ci goto cleanup; 38713498266Sopenharmony_ci } 38813498266Sopenharmony_ci 38913498266Sopenharmony_ci if(data->set.ssl.primary.verifyhost != TRUE) { 39013498266Sopenharmony_ci rc = SSH_OK; 39113498266Sopenharmony_ci goto cleanup; 39213498266Sopenharmony_ci } 39313498266Sopenharmony_ci 39413498266Sopenharmony_ci#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0) 39513498266Sopenharmony_ci /* Get the known_key from the known hosts file */ 39613498266Sopenharmony_ci vstate = ssh_session_get_known_hosts_entry(sshc->ssh_session, 39713498266Sopenharmony_ci &knownhostsentry); 39813498266Sopenharmony_ci 39913498266Sopenharmony_ci /* Case an entry was found in a known hosts file */ 40013498266Sopenharmony_ci if(knownhostsentry) { 40113498266Sopenharmony_ci if(knownhostsentry->publickey) { 40213498266Sopenharmony_ci rc = ssh_pki_export_pubkey_base64(knownhostsentry->publickey, 40313498266Sopenharmony_ci &known_base64); 40413498266Sopenharmony_ci if(rc != SSH_OK) { 40513498266Sopenharmony_ci goto cleanup; 40613498266Sopenharmony_ci } 40713498266Sopenharmony_ci knownkey.key = known_base64; 40813498266Sopenharmony_ci knownkey.len = strlen(known_base64); 40913498266Sopenharmony_ci 41013498266Sopenharmony_ci switch(ssh_key_type(knownhostsentry->publickey)) { 41113498266Sopenharmony_ci case SSH_KEYTYPE_RSA: 41213498266Sopenharmony_ci knownkey.keytype = CURLKHTYPE_RSA; 41313498266Sopenharmony_ci break; 41413498266Sopenharmony_ci case SSH_KEYTYPE_RSA1: 41513498266Sopenharmony_ci knownkey.keytype = CURLKHTYPE_RSA1; 41613498266Sopenharmony_ci break; 41713498266Sopenharmony_ci case SSH_KEYTYPE_ECDSA: 41813498266Sopenharmony_ci case SSH_KEYTYPE_ECDSA_P256: 41913498266Sopenharmony_ci case SSH_KEYTYPE_ECDSA_P384: 42013498266Sopenharmony_ci case SSH_KEYTYPE_ECDSA_P521: 42113498266Sopenharmony_ci knownkey.keytype = CURLKHTYPE_ECDSA; 42213498266Sopenharmony_ci break; 42313498266Sopenharmony_ci case SSH_KEYTYPE_ED25519: 42413498266Sopenharmony_ci knownkey.keytype = CURLKHTYPE_ED25519; 42513498266Sopenharmony_ci break; 42613498266Sopenharmony_ci case SSH_KEYTYPE_DSS: 42713498266Sopenharmony_ci knownkey.keytype = CURLKHTYPE_DSS; 42813498266Sopenharmony_ci break; 42913498266Sopenharmony_ci default: 43013498266Sopenharmony_ci rc = SSH_ERROR; 43113498266Sopenharmony_ci goto cleanup; 43213498266Sopenharmony_ci } 43313498266Sopenharmony_ci knownkeyp = &knownkey; 43413498266Sopenharmony_ci } 43513498266Sopenharmony_ci } 43613498266Sopenharmony_ci 43713498266Sopenharmony_ci switch(vstate) { 43813498266Sopenharmony_ci case SSH_KNOWN_HOSTS_OK: 43913498266Sopenharmony_ci keymatch = CURLKHMATCH_OK; 44013498266Sopenharmony_ci break; 44113498266Sopenharmony_ci case SSH_KNOWN_HOSTS_OTHER: 44213498266Sopenharmony_ci case SSH_KNOWN_HOSTS_NOT_FOUND: 44313498266Sopenharmony_ci case SSH_KNOWN_HOSTS_UNKNOWN: 44413498266Sopenharmony_ci case SSH_KNOWN_HOSTS_ERROR: 44513498266Sopenharmony_ci keymatch = CURLKHMATCH_MISSING; 44613498266Sopenharmony_ci break; 44713498266Sopenharmony_ci default: 44813498266Sopenharmony_ci keymatch = CURLKHMATCH_MISMATCH; 44913498266Sopenharmony_ci break; 45013498266Sopenharmony_ci } 45113498266Sopenharmony_ci 45213498266Sopenharmony_ci#else 45313498266Sopenharmony_ci vstate = ssh_is_server_known(sshc->ssh_session); 45413498266Sopenharmony_ci switch(vstate) { 45513498266Sopenharmony_ci case SSH_SERVER_KNOWN_OK: 45613498266Sopenharmony_ci keymatch = CURLKHMATCH_OK; 45713498266Sopenharmony_ci break; 45813498266Sopenharmony_ci case SSH_SERVER_FILE_NOT_FOUND: 45913498266Sopenharmony_ci case SSH_SERVER_NOT_KNOWN: 46013498266Sopenharmony_ci keymatch = CURLKHMATCH_MISSING; 46113498266Sopenharmony_ci break; 46213498266Sopenharmony_ci default: 46313498266Sopenharmony_ci keymatch = CURLKHMATCH_MISMATCH; 46413498266Sopenharmony_ci break; 46513498266Sopenharmony_ci } 46613498266Sopenharmony_ci#endif 46713498266Sopenharmony_ci 46813498266Sopenharmony_ci if(func) { /* use callback to determine action */ 46913498266Sopenharmony_ci rc = ssh_pki_export_pubkey_base64(pubkey, &found_base64); 47013498266Sopenharmony_ci if(rc != SSH_OK) 47113498266Sopenharmony_ci goto cleanup; 47213498266Sopenharmony_ci 47313498266Sopenharmony_ci foundkey.key = found_base64; 47413498266Sopenharmony_ci foundkey.len = strlen(found_base64); 47513498266Sopenharmony_ci 47613498266Sopenharmony_ci switch(ssh_key_type(pubkey)) { 47713498266Sopenharmony_ci case SSH_KEYTYPE_RSA: 47813498266Sopenharmony_ci foundkey.keytype = CURLKHTYPE_RSA; 47913498266Sopenharmony_ci break; 48013498266Sopenharmony_ci case SSH_KEYTYPE_RSA1: 48113498266Sopenharmony_ci foundkey.keytype = CURLKHTYPE_RSA1; 48213498266Sopenharmony_ci break; 48313498266Sopenharmony_ci case SSH_KEYTYPE_ECDSA: 48413498266Sopenharmony_ci#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0) 48513498266Sopenharmony_ci case SSH_KEYTYPE_ECDSA_P256: 48613498266Sopenharmony_ci case SSH_KEYTYPE_ECDSA_P384: 48713498266Sopenharmony_ci case SSH_KEYTYPE_ECDSA_P521: 48813498266Sopenharmony_ci#endif 48913498266Sopenharmony_ci foundkey.keytype = CURLKHTYPE_ECDSA; 49013498266Sopenharmony_ci break; 49113498266Sopenharmony_ci#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,7,0) 49213498266Sopenharmony_ci case SSH_KEYTYPE_ED25519: 49313498266Sopenharmony_ci foundkey.keytype = CURLKHTYPE_ED25519; 49413498266Sopenharmony_ci break; 49513498266Sopenharmony_ci#endif 49613498266Sopenharmony_ci case SSH_KEYTYPE_DSS: 49713498266Sopenharmony_ci foundkey.keytype = CURLKHTYPE_DSS; 49813498266Sopenharmony_ci break; 49913498266Sopenharmony_ci default: 50013498266Sopenharmony_ci rc = SSH_ERROR; 50113498266Sopenharmony_ci goto cleanup; 50213498266Sopenharmony_ci } 50313498266Sopenharmony_ci 50413498266Sopenharmony_ci Curl_set_in_callback(data, true); 50513498266Sopenharmony_ci rc = func(data, knownkeyp, /* from the knownhosts file */ 50613498266Sopenharmony_ci &foundkey, /* from the remote host */ 50713498266Sopenharmony_ci keymatch, data->set.ssh_keyfunc_userp); 50813498266Sopenharmony_ci Curl_set_in_callback(data, false); 50913498266Sopenharmony_ci 51013498266Sopenharmony_ci switch(rc) { 51113498266Sopenharmony_ci case CURLKHSTAT_FINE_ADD_TO_FILE: 51213498266Sopenharmony_ci#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0) 51313498266Sopenharmony_ci rc = ssh_session_update_known_hosts(sshc->ssh_session); 51413498266Sopenharmony_ci#else 51513498266Sopenharmony_ci rc = ssh_write_knownhost(sshc->ssh_session); 51613498266Sopenharmony_ci#endif 51713498266Sopenharmony_ci if(rc != SSH_OK) { 51813498266Sopenharmony_ci goto cleanup; 51913498266Sopenharmony_ci } 52013498266Sopenharmony_ci break; 52113498266Sopenharmony_ci case CURLKHSTAT_FINE: 52213498266Sopenharmony_ci break; 52313498266Sopenharmony_ci default: /* REJECT/DEFER */ 52413498266Sopenharmony_ci rc = SSH_ERROR; 52513498266Sopenharmony_ci goto cleanup; 52613498266Sopenharmony_ci } 52713498266Sopenharmony_ci } 52813498266Sopenharmony_ci else { 52913498266Sopenharmony_ci if(keymatch != CURLKHMATCH_OK) { 53013498266Sopenharmony_ci rc = SSH_ERROR; 53113498266Sopenharmony_ci goto cleanup; 53213498266Sopenharmony_ci } 53313498266Sopenharmony_ci } 53413498266Sopenharmony_ci rc = SSH_OK; 53513498266Sopenharmony_ci 53613498266Sopenharmony_cicleanup: 53713498266Sopenharmony_ci if(found_base64) { 53813498266Sopenharmony_ci (free)(found_base64); 53913498266Sopenharmony_ci } 54013498266Sopenharmony_ci if(known_base64) { 54113498266Sopenharmony_ci (free)(known_base64); 54213498266Sopenharmony_ci } 54313498266Sopenharmony_ci if(hash) 54413498266Sopenharmony_ci ssh_clean_pubkey_hash(&hash); 54513498266Sopenharmony_ci ssh_key_free(pubkey); 54613498266Sopenharmony_ci#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0) 54713498266Sopenharmony_ci if(knownhostsentry) { 54813498266Sopenharmony_ci ssh_knownhosts_entry_free(knownhostsentry); 54913498266Sopenharmony_ci } 55013498266Sopenharmony_ci#endif 55113498266Sopenharmony_ci return rc; 55213498266Sopenharmony_ci} 55313498266Sopenharmony_ci 55413498266Sopenharmony_ci#define MOVE_TO_ERROR_STATE(_r) do { \ 55513498266Sopenharmony_ci state(data, SSH_SESSION_DISCONNECT); \ 55613498266Sopenharmony_ci sshc->actualcode = _r; \ 55713498266Sopenharmony_ci rc = SSH_ERROR; \ 55813498266Sopenharmony_ci } while(0) 55913498266Sopenharmony_ci 56013498266Sopenharmony_ci#define MOVE_TO_SFTP_CLOSE_STATE() do { \ 56113498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); \ 56213498266Sopenharmony_ci sshc->actualcode = \ 56313498266Sopenharmony_ci sftp_error_to_CURLE(sftp_get_error(sshc->sftp_session)); \ 56413498266Sopenharmony_ci rc = SSH_ERROR; \ 56513498266Sopenharmony_ci } while(0) 56613498266Sopenharmony_ci 56713498266Sopenharmony_ci#define MOVE_TO_PASSWD_AUTH do { \ 56813498266Sopenharmony_ci if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { \ 56913498266Sopenharmony_ci rc = SSH_OK; \ 57013498266Sopenharmony_ci state(data, SSH_AUTH_PASS_INIT); \ 57113498266Sopenharmony_ci } \ 57213498266Sopenharmony_ci else { \ 57313498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); \ 57413498266Sopenharmony_ci } \ 57513498266Sopenharmony_ci } while(0) 57613498266Sopenharmony_ci 57713498266Sopenharmony_ci#define MOVE_TO_KEY_AUTH do { \ 57813498266Sopenharmony_ci if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { \ 57913498266Sopenharmony_ci rc = SSH_OK; \ 58013498266Sopenharmony_ci state(data, SSH_AUTH_KEY_INIT); \ 58113498266Sopenharmony_ci } \ 58213498266Sopenharmony_ci else { \ 58313498266Sopenharmony_ci MOVE_TO_PASSWD_AUTH; \ 58413498266Sopenharmony_ci } \ 58513498266Sopenharmony_ci } while(0) 58613498266Sopenharmony_ci 58713498266Sopenharmony_ci#define MOVE_TO_GSSAPI_AUTH do { \ 58813498266Sopenharmony_ci if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { \ 58913498266Sopenharmony_ci rc = SSH_OK; \ 59013498266Sopenharmony_ci state(data, SSH_AUTH_GSSAPI); \ 59113498266Sopenharmony_ci } \ 59213498266Sopenharmony_ci else { \ 59313498266Sopenharmony_ci MOVE_TO_KEY_AUTH; \ 59413498266Sopenharmony_ci } \ 59513498266Sopenharmony_ci } while(0) 59613498266Sopenharmony_ci 59713498266Sopenharmony_cistatic 59813498266Sopenharmony_ciint myssh_auth_interactive(struct connectdata *conn) 59913498266Sopenharmony_ci{ 60013498266Sopenharmony_ci int rc; 60113498266Sopenharmony_ci struct ssh_conn *sshc = &conn->proto.sshc; 60213498266Sopenharmony_ci int nprompts; 60313498266Sopenharmony_ci 60413498266Sopenharmony_cirestart: 60513498266Sopenharmony_ci switch(sshc->kbd_state) { 60613498266Sopenharmony_ci case 0: 60713498266Sopenharmony_ci rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL); 60813498266Sopenharmony_ci if(rc == SSH_AUTH_AGAIN) 60913498266Sopenharmony_ci return SSH_AGAIN; 61013498266Sopenharmony_ci 61113498266Sopenharmony_ci if(rc != SSH_AUTH_INFO) 61213498266Sopenharmony_ci return SSH_ERROR; 61313498266Sopenharmony_ci 61413498266Sopenharmony_ci nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session); 61513498266Sopenharmony_ci if(nprompts != 1) 61613498266Sopenharmony_ci return SSH_ERROR; 61713498266Sopenharmony_ci 61813498266Sopenharmony_ci rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, conn->passwd); 61913498266Sopenharmony_ci if(rc < 0) 62013498266Sopenharmony_ci return SSH_ERROR; 62113498266Sopenharmony_ci 62213498266Sopenharmony_ci FALLTHROUGH(); 62313498266Sopenharmony_ci case 1: 62413498266Sopenharmony_ci sshc->kbd_state = 1; 62513498266Sopenharmony_ci 62613498266Sopenharmony_ci rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL); 62713498266Sopenharmony_ci if(rc == SSH_AUTH_AGAIN) 62813498266Sopenharmony_ci return SSH_AGAIN; 62913498266Sopenharmony_ci else if(rc == SSH_AUTH_SUCCESS) 63013498266Sopenharmony_ci rc = SSH_OK; 63113498266Sopenharmony_ci else if(rc == SSH_AUTH_INFO) { 63213498266Sopenharmony_ci nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session); 63313498266Sopenharmony_ci if(nprompts) 63413498266Sopenharmony_ci return SSH_ERROR; 63513498266Sopenharmony_ci 63613498266Sopenharmony_ci sshc->kbd_state = 2; 63713498266Sopenharmony_ci goto restart; 63813498266Sopenharmony_ci } 63913498266Sopenharmony_ci else 64013498266Sopenharmony_ci rc = SSH_ERROR; 64113498266Sopenharmony_ci break; 64213498266Sopenharmony_ci case 2: 64313498266Sopenharmony_ci sshc->kbd_state = 2; 64413498266Sopenharmony_ci 64513498266Sopenharmony_ci rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL); 64613498266Sopenharmony_ci if(rc == SSH_AUTH_AGAIN) 64713498266Sopenharmony_ci return SSH_AGAIN; 64813498266Sopenharmony_ci else if(rc == SSH_AUTH_SUCCESS) 64913498266Sopenharmony_ci rc = SSH_OK; 65013498266Sopenharmony_ci else 65113498266Sopenharmony_ci rc = SSH_ERROR; 65213498266Sopenharmony_ci 65313498266Sopenharmony_ci break; 65413498266Sopenharmony_ci default: 65513498266Sopenharmony_ci return SSH_ERROR; 65613498266Sopenharmony_ci } 65713498266Sopenharmony_ci 65813498266Sopenharmony_ci sshc->kbd_state = 0; 65913498266Sopenharmony_ci return rc; 66013498266Sopenharmony_ci} 66113498266Sopenharmony_ci 66213498266Sopenharmony_ci/* 66313498266Sopenharmony_ci * ssh_statemach_act() runs the SSH state machine as far as it can without 66413498266Sopenharmony_ci * blocking and without reaching the end. The data the pointer 'block' points 66513498266Sopenharmony_ci * to will be set to TRUE if the libssh function returns SSH_AGAIN 66613498266Sopenharmony_ci * meaning it wants to be called again when the socket is ready 66713498266Sopenharmony_ci */ 66813498266Sopenharmony_cistatic CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block) 66913498266Sopenharmony_ci{ 67013498266Sopenharmony_ci CURLcode result = CURLE_OK; 67113498266Sopenharmony_ci struct connectdata *conn = data->conn; 67213498266Sopenharmony_ci struct SSHPROTO *protop = data->req.p.ssh; 67313498266Sopenharmony_ci struct ssh_conn *sshc = &conn->proto.sshc; 67413498266Sopenharmony_ci curl_socket_t sock = conn->sock[FIRSTSOCKET]; 67513498266Sopenharmony_ci int rc = SSH_NO_ERROR, err; 67613498266Sopenharmony_ci int seekerr = CURL_SEEKFUNC_OK; 67713498266Sopenharmony_ci const char *err_msg; 67813498266Sopenharmony_ci *block = 0; /* we're not blocking by default */ 67913498266Sopenharmony_ci 68013498266Sopenharmony_ci do { 68113498266Sopenharmony_ci 68213498266Sopenharmony_ci switch(sshc->state) { 68313498266Sopenharmony_ci case SSH_INIT: 68413498266Sopenharmony_ci sshc->secondCreateDirs = 0; 68513498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 68613498266Sopenharmony_ci sshc->actualcode = CURLE_OK; 68713498266Sopenharmony_ci 68813498266Sopenharmony_ci#if 0 68913498266Sopenharmony_ci ssh_set_log_level(SSH_LOG_PROTOCOL); 69013498266Sopenharmony_ci#endif 69113498266Sopenharmony_ci 69213498266Sopenharmony_ci /* Set libssh to non-blocking, since everything internally is 69313498266Sopenharmony_ci non-blocking */ 69413498266Sopenharmony_ci ssh_set_blocking(sshc->ssh_session, 0); 69513498266Sopenharmony_ci 69613498266Sopenharmony_ci state(data, SSH_S_STARTUP); 69713498266Sopenharmony_ci FALLTHROUGH(); 69813498266Sopenharmony_ci 69913498266Sopenharmony_ci case SSH_S_STARTUP: 70013498266Sopenharmony_ci rc = ssh_connect(sshc->ssh_session); 70113498266Sopenharmony_ci if(rc == SSH_AGAIN) 70213498266Sopenharmony_ci break; 70313498266Sopenharmony_ci 70413498266Sopenharmony_ci if(rc != SSH_OK) { 70513498266Sopenharmony_ci failf(data, "Failure establishing ssh session"); 70613498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_FAILED_INIT); 70713498266Sopenharmony_ci break; 70813498266Sopenharmony_ci } 70913498266Sopenharmony_ci 71013498266Sopenharmony_ci state(data, SSH_HOSTKEY); 71113498266Sopenharmony_ci 71213498266Sopenharmony_ci FALLTHROUGH(); 71313498266Sopenharmony_ci case SSH_HOSTKEY: 71413498266Sopenharmony_ci 71513498266Sopenharmony_ci rc = myssh_is_known(data); 71613498266Sopenharmony_ci if(rc != SSH_OK) { 71713498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_PEER_FAILED_VERIFICATION); 71813498266Sopenharmony_ci break; 71913498266Sopenharmony_ci } 72013498266Sopenharmony_ci 72113498266Sopenharmony_ci state(data, SSH_AUTHLIST); 72213498266Sopenharmony_ci FALLTHROUGH(); 72313498266Sopenharmony_ci case SSH_AUTHLIST:{ 72413498266Sopenharmony_ci sshc->authed = FALSE; 72513498266Sopenharmony_ci 72613498266Sopenharmony_ci rc = ssh_userauth_none(sshc->ssh_session, NULL); 72713498266Sopenharmony_ci if(rc == SSH_AUTH_AGAIN) { 72813498266Sopenharmony_ci rc = SSH_AGAIN; 72913498266Sopenharmony_ci break; 73013498266Sopenharmony_ci } 73113498266Sopenharmony_ci 73213498266Sopenharmony_ci if(rc == SSH_AUTH_SUCCESS) { 73313498266Sopenharmony_ci sshc->authed = TRUE; 73413498266Sopenharmony_ci infof(data, "Authenticated with none"); 73513498266Sopenharmony_ci state(data, SSH_AUTH_DONE); 73613498266Sopenharmony_ci break; 73713498266Sopenharmony_ci } 73813498266Sopenharmony_ci else if(rc == SSH_AUTH_ERROR) { 73913498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); 74013498266Sopenharmony_ci break; 74113498266Sopenharmony_ci } 74213498266Sopenharmony_ci 74313498266Sopenharmony_ci sshc->auth_methods = ssh_userauth_list(sshc->ssh_session, NULL); 74413498266Sopenharmony_ci if(sshc->auth_methods) 74513498266Sopenharmony_ci infof(data, "SSH authentication methods available: %s%s%s%s", 74613498266Sopenharmony_ci sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY ? 74713498266Sopenharmony_ci "public key, ": "", 74813498266Sopenharmony_ci sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC ? 74913498266Sopenharmony_ci "GSSAPI, " : "", 75013498266Sopenharmony_ci sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE ? 75113498266Sopenharmony_ci "keyboard-interactive, " : "", 75213498266Sopenharmony_ci sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD ? 75313498266Sopenharmony_ci "password": ""); 75413498266Sopenharmony_ci if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) { 75513498266Sopenharmony_ci state(data, SSH_AUTH_PKEY_INIT); 75613498266Sopenharmony_ci infof(data, "Authentication using SSH public key file"); 75713498266Sopenharmony_ci } 75813498266Sopenharmony_ci else if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { 75913498266Sopenharmony_ci state(data, SSH_AUTH_GSSAPI); 76013498266Sopenharmony_ci } 76113498266Sopenharmony_ci else if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { 76213498266Sopenharmony_ci state(data, SSH_AUTH_KEY_INIT); 76313498266Sopenharmony_ci } 76413498266Sopenharmony_ci else if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { 76513498266Sopenharmony_ci state(data, SSH_AUTH_PASS_INIT); 76613498266Sopenharmony_ci } 76713498266Sopenharmony_ci else { /* unsupported authentication method */ 76813498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); 76913498266Sopenharmony_ci break; 77013498266Sopenharmony_ci } 77113498266Sopenharmony_ci 77213498266Sopenharmony_ci break; 77313498266Sopenharmony_ci } 77413498266Sopenharmony_ci case SSH_AUTH_PKEY_INIT: 77513498266Sopenharmony_ci if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY)) { 77613498266Sopenharmony_ci MOVE_TO_GSSAPI_AUTH; 77713498266Sopenharmony_ci break; 77813498266Sopenharmony_ci } 77913498266Sopenharmony_ci 78013498266Sopenharmony_ci /* Two choices, (1) private key was given on CMD, 78113498266Sopenharmony_ci * (2) use the "default" keys. */ 78213498266Sopenharmony_ci if(data->set.str[STRING_SSH_PRIVATE_KEY]) { 78313498266Sopenharmony_ci if(sshc->pubkey && !data->set.ssl.key_passwd) { 78413498266Sopenharmony_ci rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL, 78513498266Sopenharmony_ci sshc->pubkey); 78613498266Sopenharmony_ci if(rc == SSH_AUTH_AGAIN) { 78713498266Sopenharmony_ci rc = SSH_AGAIN; 78813498266Sopenharmony_ci break; 78913498266Sopenharmony_ci } 79013498266Sopenharmony_ci 79113498266Sopenharmony_ci if(rc != SSH_OK) { 79213498266Sopenharmony_ci MOVE_TO_GSSAPI_AUTH; 79313498266Sopenharmony_ci break; 79413498266Sopenharmony_ci } 79513498266Sopenharmony_ci } 79613498266Sopenharmony_ci 79713498266Sopenharmony_ci rc = ssh_pki_import_privkey_file(data-> 79813498266Sopenharmony_ci set.str[STRING_SSH_PRIVATE_KEY], 79913498266Sopenharmony_ci data->set.ssl.key_passwd, NULL, 80013498266Sopenharmony_ci NULL, &sshc->privkey); 80113498266Sopenharmony_ci if(rc != SSH_OK) { 80213498266Sopenharmony_ci failf(data, "Could not load private key file %s", 80313498266Sopenharmony_ci data->set.str[STRING_SSH_PRIVATE_KEY]); 80413498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); 80513498266Sopenharmony_ci break; 80613498266Sopenharmony_ci } 80713498266Sopenharmony_ci 80813498266Sopenharmony_ci state(data, SSH_AUTH_PKEY); 80913498266Sopenharmony_ci break; 81013498266Sopenharmony_ci 81113498266Sopenharmony_ci } 81213498266Sopenharmony_ci else { 81313498266Sopenharmony_ci rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL, 81413498266Sopenharmony_ci data->set.ssl.key_passwd); 81513498266Sopenharmony_ci if(rc == SSH_AUTH_AGAIN) { 81613498266Sopenharmony_ci rc = SSH_AGAIN; 81713498266Sopenharmony_ci break; 81813498266Sopenharmony_ci } 81913498266Sopenharmony_ci if(rc == SSH_AUTH_SUCCESS) { 82013498266Sopenharmony_ci rc = SSH_OK; 82113498266Sopenharmony_ci sshc->authed = TRUE; 82213498266Sopenharmony_ci infof(data, "Completed public key authentication"); 82313498266Sopenharmony_ci state(data, SSH_AUTH_DONE); 82413498266Sopenharmony_ci break; 82513498266Sopenharmony_ci } 82613498266Sopenharmony_ci 82713498266Sopenharmony_ci MOVE_TO_GSSAPI_AUTH; 82813498266Sopenharmony_ci } 82913498266Sopenharmony_ci break; 83013498266Sopenharmony_ci case SSH_AUTH_PKEY: 83113498266Sopenharmony_ci rc = ssh_userauth_publickey(sshc->ssh_session, NULL, sshc->privkey); 83213498266Sopenharmony_ci if(rc == SSH_AUTH_AGAIN) { 83313498266Sopenharmony_ci rc = SSH_AGAIN; 83413498266Sopenharmony_ci break; 83513498266Sopenharmony_ci } 83613498266Sopenharmony_ci 83713498266Sopenharmony_ci if(rc == SSH_AUTH_SUCCESS) { 83813498266Sopenharmony_ci sshc->authed = TRUE; 83913498266Sopenharmony_ci infof(data, "Completed public key authentication"); 84013498266Sopenharmony_ci state(data, SSH_AUTH_DONE); 84113498266Sopenharmony_ci break; 84213498266Sopenharmony_ci } 84313498266Sopenharmony_ci else { 84413498266Sopenharmony_ci infof(data, "Failed public key authentication (rc: %d)", rc); 84513498266Sopenharmony_ci MOVE_TO_GSSAPI_AUTH; 84613498266Sopenharmony_ci } 84713498266Sopenharmony_ci break; 84813498266Sopenharmony_ci 84913498266Sopenharmony_ci case SSH_AUTH_GSSAPI: 85013498266Sopenharmony_ci if(!(data->set.ssh_auth_types & CURLSSH_AUTH_GSSAPI)) { 85113498266Sopenharmony_ci MOVE_TO_KEY_AUTH; 85213498266Sopenharmony_ci break; 85313498266Sopenharmony_ci } 85413498266Sopenharmony_ci 85513498266Sopenharmony_ci rc = ssh_userauth_gssapi(sshc->ssh_session); 85613498266Sopenharmony_ci if(rc == SSH_AUTH_AGAIN) { 85713498266Sopenharmony_ci rc = SSH_AGAIN; 85813498266Sopenharmony_ci break; 85913498266Sopenharmony_ci } 86013498266Sopenharmony_ci 86113498266Sopenharmony_ci if(rc == SSH_AUTH_SUCCESS) { 86213498266Sopenharmony_ci rc = SSH_OK; 86313498266Sopenharmony_ci sshc->authed = TRUE; 86413498266Sopenharmony_ci infof(data, "Completed gssapi authentication"); 86513498266Sopenharmony_ci state(data, SSH_AUTH_DONE); 86613498266Sopenharmony_ci break; 86713498266Sopenharmony_ci } 86813498266Sopenharmony_ci 86913498266Sopenharmony_ci MOVE_TO_KEY_AUTH; 87013498266Sopenharmony_ci break; 87113498266Sopenharmony_ci 87213498266Sopenharmony_ci case SSH_AUTH_KEY_INIT: 87313498266Sopenharmony_ci if(data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) { 87413498266Sopenharmony_ci state(data, SSH_AUTH_KEY); 87513498266Sopenharmony_ci } 87613498266Sopenharmony_ci else { 87713498266Sopenharmony_ci MOVE_TO_PASSWD_AUTH; 87813498266Sopenharmony_ci } 87913498266Sopenharmony_ci break; 88013498266Sopenharmony_ci 88113498266Sopenharmony_ci case SSH_AUTH_KEY: 88213498266Sopenharmony_ci /* keyboard-interactive authentication */ 88313498266Sopenharmony_ci rc = myssh_auth_interactive(conn); 88413498266Sopenharmony_ci if(rc == SSH_AGAIN) { 88513498266Sopenharmony_ci break; 88613498266Sopenharmony_ci } 88713498266Sopenharmony_ci if(rc == SSH_OK) { 88813498266Sopenharmony_ci sshc->authed = TRUE; 88913498266Sopenharmony_ci infof(data, "completed keyboard interactive authentication"); 89013498266Sopenharmony_ci state(data, SSH_AUTH_DONE); 89113498266Sopenharmony_ci } 89213498266Sopenharmony_ci else { 89313498266Sopenharmony_ci MOVE_TO_PASSWD_AUTH; 89413498266Sopenharmony_ci } 89513498266Sopenharmony_ci break; 89613498266Sopenharmony_ci 89713498266Sopenharmony_ci case SSH_AUTH_PASS_INIT: 89813498266Sopenharmony_ci if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD)) { 89913498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); 90013498266Sopenharmony_ci break; 90113498266Sopenharmony_ci } 90213498266Sopenharmony_ci state(data, SSH_AUTH_PASS); 90313498266Sopenharmony_ci FALLTHROUGH(); 90413498266Sopenharmony_ci 90513498266Sopenharmony_ci case SSH_AUTH_PASS: 90613498266Sopenharmony_ci rc = ssh_userauth_password(sshc->ssh_session, NULL, conn->passwd); 90713498266Sopenharmony_ci if(rc == SSH_AUTH_AGAIN) { 90813498266Sopenharmony_ci rc = SSH_AGAIN; 90913498266Sopenharmony_ci break; 91013498266Sopenharmony_ci } 91113498266Sopenharmony_ci 91213498266Sopenharmony_ci if(rc == SSH_AUTH_SUCCESS) { 91313498266Sopenharmony_ci sshc->authed = TRUE; 91413498266Sopenharmony_ci infof(data, "Completed password authentication"); 91513498266Sopenharmony_ci state(data, SSH_AUTH_DONE); 91613498266Sopenharmony_ci } 91713498266Sopenharmony_ci else { 91813498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); 91913498266Sopenharmony_ci } 92013498266Sopenharmony_ci break; 92113498266Sopenharmony_ci 92213498266Sopenharmony_ci case SSH_AUTH_DONE: 92313498266Sopenharmony_ci if(!sshc->authed) { 92413498266Sopenharmony_ci failf(data, "Authentication failure"); 92513498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); 92613498266Sopenharmony_ci break; 92713498266Sopenharmony_ci } 92813498266Sopenharmony_ci 92913498266Sopenharmony_ci /* 93013498266Sopenharmony_ci * At this point we have an authenticated ssh session. 93113498266Sopenharmony_ci */ 93213498266Sopenharmony_ci infof(data, "Authentication complete"); 93313498266Sopenharmony_ci 93413498266Sopenharmony_ci Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */ 93513498266Sopenharmony_ci 93613498266Sopenharmony_ci conn->sockfd = sock; 93713498266Sopenharmony_ci conn->writesockfd = CURL_SOCKET_BAD; 93813498266Sopenharmony_ci 93913498266Sopenharmony_ci if(conn->handler->protocol == CURLPROTO_SFTP) { 94013498266Sopenharmony_ci state(data, SSH_SFTP_INIT); 94113498266Sopenharmony_ci break; 94213498266Sopenharmony_ci } 94313498266Sopenharmony_ci infof(data, "SSH CONNECT phase done"); 94413498266Sopenharmony_ci state(data, SSH_STOP); 94513498266Sopenharmony_ci break; 94613498266Sopenharmony_ci 94713498266Sopenharmony_ci case SSH_SFTP_INIT: 94813498266Sopenharmony_ci ssh_set_blocking(sshc->ssh_session, 1); 94913498266Sopenharmony_ci 95013498266Sopenharmony_ci sshc->sftp_session = sftp_new(sshc->ssh_session); 95113498266Sopenharmony_ci if(!sshc->sftp_session) { 95213498266Sopenharmony_ci failf(data, "Failure initializing sftp session: %s", 95313498266Sopenharmony_ci ssh_get_error(sshc->ssh_session)); 95413498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT); 95513498266Sopenharmony_ci break; 95613498266Sopenharmony_ci } 95713498266Sopenharmony_ci 95813498266Sopenharmony_ci rc = sftp_init(sshc->sftp_session); 95913498266Sopenharmony_ci if(rc != SSH_OK) { 96013498266Sopenharmony_ci failf(data, "Failure initializing sftp session: %s", 96113498266Sopenharmony_ci ssh_get_error(sshc->ssh_session)); 96213498266Sopenharmony_ci MOVE_TO_ERROR_STATE(sftp_error_to_CURLE(SSH_FX_FAILURE)); 96313498266Sopenharmony_ci break; 96413498266Sopenharmony_ci } 96513498266Sopenharmony_ci state(data, SSH_SFTP_REALPATH); 96613498266Sopenharmony_ci FALLTHROUGH(); 96713498266Sopenharmony_ci case SSH_SFTP_REALPATH: 96813498266Sopenharmony_ci /* 96913498266Sopenharmony_ci * Get the "home" directory 97013498266Sopenharmony_ci */ 97113498266Sopenharmony_ci sshc->homedir = sftp_canonicalize_path(sshc->sftp_session, "."); 97213498266Sopenharmony_ci if(!sshc->homedir) { 97313498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT); 97413498266Sopenharmony_ci break; 97513498266Sopenharmony_ci } 97613498266Sopenharmony_ci data->state.most_recent_ftp_entrypath = sshc->homedir; 97713498266Sopenharmony_ci 97813498266Sopenharmony_ci /* This is the last step in the SFTP connect phase. Do note that while 97913498266Sopenharmony_ci we get the homedir here, we get the "workingpath" in the DO action 98013498266Sopenharmony_ci since the homedir will remain the same between request but the 98113498266Sopenharmony_ci working path will not. */ 98213498266Sopenharmony_ci DEBUGF(infof(data, "SSH CONNECT phase done")); 98313498266Sopenharmony_ci state(data, SSH_STOP); 98413498266Sopenharmony_ci break; 98513498266Sopenharmony_ci 98613498266Sopenharmony_ci case SSH_SFTP_QUOTE_INIT: 98713498266Sopenharmony_ci result = Curl_getworkingpath(data, sshc->homedir, &protop->path); 98813498266Sopenharmony_ci if(result) { 98913498266Sopenharmony_ci sshc->actualcode = result; 99013498266Sopenharmony_ci state(data, SSH_STOP); 99113498266Sopenharmony_ci break; 99213498266Sopenharmony_ci } 99313498266Sopenharmony_ci 99413498266Sopenharmony_ci if(data->set.quote) { 99513498266Sopenharmony_ci infof(data, "Sending quote commands"); 99613498266Sopenharmony_ci sshc->quote_item = data->set.quote; 99713498266Sopenharmony_ci state(data, SSH_SFTP_QUOTE); 99813498266Sopenharmony_ci } 99913498266Sopenharmony_ci else { 100013498266Sopenharmony_ci state(data, SSH_SFTP_GETINFO); 100113498266Sopenharmony_ci } 100213498266Sopenharmony_ci break; 100313498266Sopenharmony_ci 100413498266Sopenharmony_ci case SSH_SFTP_POSTQUOTE_INIT: 100513498266Sopenharmony_ci if(data->set.postquote) { 100613498266Sopenharmony_ci infof(data, "Sending quote commands"); 100713498266Sopenharmony_ci sshc->quote_item = data->set.postquote; 100813498266Sopenharmony_ci state(data, SSH_SFTP_QUOTE); 100913498266Sopenharmony_ci } 101013498266Sopenharmony_ci else { 101113498266Sopenharmony_ci state(data, SSH_STOP); 101213498266Sopenharmony_ci } 101313498266Sopenharmony_ci break; 101413498266Sopenharmony_ci 101513498266Sopenharmony_ci case SSH_SFTP_QUOTE: 101613498266Sopenharmony_ci /* Send any quote commands */ 101713498266Sopenharmony_ci sftp_quote(data); 101813498266Sopenharmony_ci break; 101913498266Sopenharmony_ci 102013498266Sopenharmony_ci case SSH_SFTP_NEXT_QUOTE: 102113498266Sopenharmony_ci Curl_safefree(sshc->quote_path1); 102213498266Sopenharmony_ci Curl_safefree(sshc->quote_path2); 102313498266Sopenharmony_ci 102413498266Sopenharmony_ci sshc->quote_item = sshc->quote_item->next; 102513498266Sopenharmony_ci 102613498266Sopenharmony_ci if(sshc->quote_item) { 102713498266Sopenharmony_ci state(data, SSH_SFTP_QUOTE); 102813498266Sopenharmony_ci } 102913498266Sopenharmony_ci else { 103013498266Sopenharmony_ci if(sshc->nextstate != SSH_NO_STATE) { 103113498266Sopenharmony_ci state(data, sshc->nextstate); 103213498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 103313498266Sopenharmony_ci } 103413498266Sopenharmony_ci else { 103513498266Sopenharmony_ci state(data, SSH_SFTP_GETINFO); 103613498266Sopenharmony_ci } 103713498266Sopenharmony_ci } 103813498266Sopenharmony_ci break; 103913498266Sopenharmony_ci 104013498266Sopenharmony_ci case SSH_SFTP_QUOTE_STAT: 104113498266Sopenharmony_ci sftp_quote_stat(data); 104213498266Sopenharmony_ci break; 104313498266Sopenharmony_ci 104413498266Sopenharmony_ci case SSH_SFTP_QUOTE_SETSTAT: 104513498266Sopenharmony_ci rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2, 104613498266Sopenharmony_ci sshc->quote_attrs); 104713498266Sopenharmony_ci if(rc && !sshc->acceptfail) { 104813498266Sopenharmony_ci Curl_safefree(sshc->quote_path1); 104913498266Sopenharmony_ci Curl_safefree(sshc->quote_path2); 105013498266Sopenharmony_ci failf(data, "Attempt to set SFTP stats failed: %s", 105113498266Sopenharmony_ci ssh_get_error(sshc->ssh_session)); 105213498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 105313498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 105413498266Sopenharmony_ci sshc->actualcode = CURLE_QUOTE_ERROR; 105513498266Sopenharmony_ci /* sshc->actualcode = sftp_error_to_CURLE(err); 105613498266Sopenharmony_ci * we do not send the actual error; we return 105713498266Sopenharmony_ci * the error the libssh2 backend is returning */ 105813498266Sopenharmony_ci break; 105913498266Sopenharmony_ci } 106013498266Sopenharmony_ci state(data, SSH_SFTP_NEXT_QUOTE); 106113498266Sopenharmony_ci break; 106213498266Sopenharmony_ci 106313498266Sopenharmony_ci case SSH_SFTP_QUOTE_SYMLINK: 106413498266Sopenharmony_ci rc = sftp_symlink(sshc->sftp_session, sshc->quote_path2, 106513498266Sopenharmony_ci sshc->quote_path1); 106613498266Sopenharmony_ci if(rc && !sshc->acceptfail) { 106713498266Sopenharmony_ci Curl_safefree(sshc->quote_path1); 106813498266Sopenharmony_ci Curl_safefree(sshc->quote_path2); 106913498266Sopenharmony_ci failf(data, "symlink command failed: %s", 107013498266Sopenharmony_ci ssh_get_error(sshc->ssh_session)); 107113498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 107213498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 107313498266Sopenharmony_ci sshc->actualcode = CURLE_QUOTE_ERROR; 107413498266Sopenharmony_ci break; 107513498266Sopenharmony_ci } 107613498266Sopenharmony_ci state(data, SSH_SFTP_NEXT_QUOTE); 107713498266Sopenharmony_ci break; 107813498266Sopenharmony_ci 107913498266Sopenharmony_ci case SSH_SFTP_QUOTE_MKDIR: 108013498266Sopenharmony_ci rc = sftp_mkdir(sshc->sftp_session, sshc->quote_path1, 108113498266Sopenharmony_ci (mode_t)data->set.new_directory_perms); 108213498266Sopenharmony_ci if(rc && !sshc->acceptfail) { 108313498266Sopenharmony_ci Curl_safefree(sshc->quote_path1); 108413498266Sopenharmony_ci failf(data, "mkdir command failed: %s", 108513498266Sopenharmony_ci ssh_get_error(sshc->ssh_session)); 108613498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 108713498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 108813498266Sopenharmony_ci sshc->actualcode = CURLE_QUOTE_ERROR; 108913498266Sopenharmony_ci break; 109013498266Sopenharmony_ci } 109113498266Sopenharmony_ci state(data, SSH_SFTP_NEXT_QUOTE); 109213498266Sopenharmony_ci break; 109313498266Sopenharmony_ci 109413498266Sopenharmony_ci case SSH_SFTP_QUOTE_RENAME: 109513498266Sopenharmony_ci rc = sftp_rename(sshc->sftp_session, sshc->quote_path1, 109613498266Sopenharmony_ci sshc->quote_path2); 109713498266Sopenharmony_ci if(rc && !sshc->acceptfail) { 109813498266Sopenharmony_ci Curl_safefree(sshc->quote_path1); 109913498266Sopenharmony_ci Curl_safefree(sshc->quote_path2); 110013498266Sopenharmony_ci failf(data, "rename command failed: %s", 110113498266Sopenharmony_ci ssh_get_error(sshc->ssh_session)); 110213498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 110313498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 110413498266Sopenharmony_ci sshc->actualcode = CURLE_QUOTE_ERROR; 110513498266Sopenharmony_ci break; 110613498266Sopenharmony_ci } 110713498266Sopenharmony_ci state(data, SSH_SFTP_NEXT_QUOTE); 110813498266Sopenharmony_ci break; 110913498266Sopenharmony_ci 111013498266Sopenharmony_ci case SSH_SFTP_QUOTE_RMDIR: 111113498266Sopenharmony_ci rc = sftp_rmdir(sshc->sftp_session, sshc->quote_path1); 111213498266Sopenharmony_ci if(rc && !sshc->acceptfail) { 111313498266Sopenharmony_ci Curl_safefree(sshc->quote_path1); 111413498266Sopenharmony_ci failf(data, "rmdir command failed: %s", 111513498266Sopenharmony_ci ssh_get_error(sshc->ssh_session)); 111613498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 111713498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 111813498266Sopenharmony_ci sshc->actualcode = CURLE_QUOTE_ERROR; 111913498266Sopenharmony_ci break; 112013498266Sopenharmony_ci } 112113498266Sopenharmony_ci state(data, SSH_SFTP_NEXT_QUOTE); 112213498266Sopenharmony_ci break; 112313498266Sopenharmony_ci 112413498266Sopenharmony_ci case SSH_SFTP_QUOTE_UNLINK: 112513498266Sopenharmony_ci rc = sftp_unlink(sshc->sftp_session, sshc->quote_path1); 112613498266Sopenharmony_ci if(rc && !sshc->acceptfail) { 112713498266Sopenharmony_ci Curl_safefree(sshc->quote_path1); 112813498266Sopenharmony_ci failf(data, "rm command failed: %s", 112913498266Sopenharmony_ci ssh_get_error(sshc->ssh_session)); 113013498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 113113498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 113213498266Sopenharmony_ci sshc->actualcode = CURLE_QUOTE_ERROR; 113313498266Sopenharmony_ci break; 113413498266Sopenharmony_ci } 113513498266Sopenharmony_ci state(data, SSH_SFTP_NEXT_QUOTE); 113613498266Sopenharmony_ci break; 113713498266Sopenharmony_ci 113813498266Sopenharmony_ci case SSH_SFTP_QUOTE_STATVFS: 113913498266Sopenharmony_ci { 114013498266Sopenharmony_ci sftp_statvfs_t statvfs; 114113498266Sopenharmony_ci 114213498266Sopenharmony_ci statvfs = sftp_statvfs(sshc->sftp_session, sshc->quote_path1); 114313498266Sopenharmony_ci if(!statvfs && !sshc->acceptfail) { 114413498266Sopenharmony_ci Curl_safefree(sshc->quote_path1); 114513498266Sopenharmony_ci failf(data, "statvfs command failed: %s", 114613498266Sopenharmony_ci ssh_get_error(sshc->ssh_session)); 114713498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 114813498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 114913498266Sopenharmony_ci sshc->actualcode = CURLE_QUOTE_ERROR; 115013498266Sopenharmony_ci break; 115113498266Sopenharmony_ci } 115213498266Sopenharmony_ci else if(statvfs) { 115313498266Sopenharmony_ci #ifdef _MSC_VER 115413498266Sopenharmony_ci #define CURL_LIBSSH_VFS_SIZE_MASK "I64u" 115513498266Sopenharmony_ci #else 115613498266Sopenharmony_ci #define CURL_LIBSSH_VFS_SIZE_MASK PRIu64 115713498266Sopenharmony_ci #endif 115813498266Sopenharmony_ci char *tmp = aprintf("statvfs:\n" 115913498266Sopenharmony_ci "f_bsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" 116013498266Sopenharmony_ci "f_frsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" 116113498266Sopenharmony_ci "f_blocks: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" 116213498266Sopenharmony_ci "f_bfree: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" 116313498266Sopenharmony_ci "f_bavail: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" 116413498266Sopenharmony_ci "f_files: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" 116513498266Sopenharmony_ci "f_ffree: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" 116613498266Sopenharmony_ci "f_favail: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" 116713498266Sopenharmony_ci "f_fsid: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" 116813498266Sopenharmony_ci "f_flag: %" CURL_LIBSSH_VFS_SIZE_MASK "\n" 116913498266Sopenharmony_ci "f_namemax: %" CURL_LIBSSH_VFS_SIZE_MASK "\n", 117013498266Sopenharmony_ci statvfs->f_bsize, statvfs->f_frsize, 117113498266Sopenharmony_ci statvfs->f_blocks, statvfs->f_bfree, 117213498266Sopenharmony_ci statvfs->f_bavail, statvfs->f_files, 117313498266Sopenharmony_ci statvfs->f_ffree, statvfs->f_favail, 117413498266Sopenharmony_ci statvfs->f_fsid, statvfs->f_flag, 117513498266Sopenharmony_ci statvfs->f_namemax); 117613498266Sopenharmony_ci sftp_statvfs_free(statvfs); 117713498266Sopenharmony_ci 117813498266Sopenharmony_ci if(!tmp) { 117913498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 118013498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 118113498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 118213498266Sopenharmony_ci break; 118313498266Sopenharmony_ci } 118413498266Sopenharmony_ci 118513498266Sopenharmony_ci result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); 118613498266Sopenharmony_ci free(tmp); 118713498266Sopenharmony_ci if(result) { 118813498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 118913498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 119013498266Sopenharmony_ci sshc->actualcode = result; 119113498266Sopenharmony_ci } 119213498266Sopenharmony_ci } 119313498266Sopenharmony_ci state(data, SSH_SFTP_NEXT_QUOTE); 119413498266Sopenharmony_ci break; 119513498266Sopenharmony_ci } 119613498266Sopenharmony_ci 119713498266Sopenharmony_ci case SSH_SFTP_GETINFO: 119813498266Sopenharmony_ci if(data->set.get_filetime) { 119913498266Sopenharmony_ci state(data, SSH_SFTP_FILETIME); 120013498266Sopenharmony_ci } 120113498266Sopenharmony_ci else { 120213498266Sopenharmony_ci state(data, SSH_SFTP_TRANS_INIT); 120313498266Sopenharmony_ci } 120413498266Sopenharmony_ci break; 120513498266Sopenharmony_ci 120613498266Sopenharmony_ci case SSH_SFTP_FILETIME: 120713498266Sopenharmony_ci { 120813498266Sopenharmony_ci sftp_attributes attrs; 120913498266Sopenharmony_ci 121013498266Sopenharmony_ci attrs = sftp_stat(sshc->sftp_session, protop->path); 121113498266Sopenharmony_ci if(attrs) { 121213498266Sopenharmony_ci data->info.filetime = attrs->mtime; 121313498266Sopenharmony_ci sftp_attributes_free(attrs); 121413498266Sopenharmony_ci } 121513498266Sopenharmony_ci 121613498266Sopenharmony_ci state(data, SSH_SFTP_TRANS_INIT); 121713498266Sopenharmony_ci break; 121813498266Sopenharmony_ci } 121913498266Sopenharmony_ci 122013498266Sopenharmony_ci case SSH_SFTP_TRANS_INIT: 122113498266Sopenharmony_ci if(data->state.upload) 122213498266Sopenharmony_ci state(data, SSH_SFTP_UPLOAD_INIT); 122313498266Sopenharmony_ci else { 122413498266Sopenharmony_ci if(protop->path[strlen(protop->path)-1] == '/') 122513498266Sopenharmony_ci state(data, SSH_SFTP_READDIR_INIT); 122613498266Sopenharmony_ci else 122713498266Sopenharmony_ci state(data, SSH_SFTP_DOWNLOAD_INIT); 122813498266Sopenharmony_ci } 122913498266Sopenharmony_ci break; 123013498266Sopenharmony_ci 123113498266Sopenharmony_ci case SSH_SFTP_UPLOAD_INIT: 123213498266Sopenharmony_ci { 123313498266Sopenharmony_ci int flags; 123413498266Sopenharmony_ci 123513498266Sopenharmony_ci if(data->state.resume_from) { 123613498266Sopenharmony_ci sftp_attributes attrs; 123713498266Sopenharmony_ci 123813498266Sopenharmony_ci if(data->state.resume_from < 0) { 123913498266Sopenharmony_ci attrs = sftp_stat(sshc->sftp_session, protop->path); 124013498266Sopenharmony_ci if(attrs) { 124113498266Sopenharmony_ci curl_off_t size = attrs->size; 124213498266Sopenharmony_ci if(size < 0) { 124313498266Sopenharmony_ci failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); 124413498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME); 124513498266Sopenharmony_ci break; 124613498266Sopenharmony_ci } 124713498266Sopenharmony_ci data->state.resume_from = attrs->size; 124813498266Sopenharmony_ci 124913498266Sopenharmony_ci sftp_attributes_free(attrs); 125013498266Sopenharmony_ci } 125113498266Sopenharmony_ci else { 125213498266Sopenharmony_ci data->state.resume_from = 0; 125313498266Sopenharmony_ci } 125413498266Sopenharmony_ci } 125513498266Sopenharmony_ci } 125613498266Sopenharmony_ci 125713498266Sopenharmony_ci if(data->set.remote_append) 125813498266Sopenharmony_ci /* Try to open for append, but create if nonexisting */ 125913498266Sopenharmony_ci flags = O_WRONLY|O_CREAT|O_APPEND; 126013498266Sopenharmony_ci else if(data->state.resume_from > 0) 126113498266Sopenharmony_ci /* If we have restart position then open for append */ 126213498266Sopenharmony_ci flags = O_WRONLY|O_APPEND; 126313498266Sopenharmony_ci else 126413498266Sopenharmony_ci /* Clear file before writing (normal behavior) */ 126513498266Sopenharmony_ci flags = O_WRONLY|O_CREAT|O_TRUNC; 126613498266Sopenharmony_ci 126713498266Sopenharmony_ci if(sshc->sftp_file) 126813498266Sopenharmony_ci sftp_close(sshc->sftp_file); 126913498266Sopenharmony_ci sshc->sftp_file = 127013498266Sopenharmony_ci sftp_open(sshc->sftp_session, protop->path, 127113498266Sopenharmony_ci flags, (mode_t)data->set.new_file_perms); 127213498266Sopenharmony_ci if(!sshc->sftp_file) { 127313498266Sopenharmony_ci err = sftp_get_error(sshc->sftp_session); 127413498266Sopenharmony_ci 127513498266Sopenharmony_ci if(((err == SSH_FX_NO_SUCH_FILE || err == SSH_FX_FAILURE || 127613498266Sopenharmony_ci err == SSH_FX_NO_SUCH_PATH)) && 127713498266Sopenharmony_ci (data->set.ftp_create_missing_dirs && 127813498266Sopenharmony_ci (strlen(protop->path) > 1))) { 127913498266Sopenharmony_ci /* try to create the path remotely */ 128013498266Sopenharmony_ci rc = 0; 128113498266Sopenharmony_ci sshc->secondCreateDirs = 1; 128213498266Sopenharmony_ci state(data, SSH_SFTP_CREATE_DIRS_INIT); 128313498266Sopenharmony_ci break; 128413498266Sopenharmony_ci } 128513498266Sopenharmony_ci else { 128613498266Sopenharmony_ci MOVE_TO_SFTP_CLOSE_STATE(); 128713498266Sopenharmony_ci break; 128813498266Sopenharmony_ci } 128913498266Sopenharmony_ci } 129013498266Sopenharmony_ci 129113498266Sopenharmony_ci /* If we have a restart point then we need to seek to the correct 129213498266Sopenharmony_ci position. */ 129313498266Sopenharmony_ci if(data->state.resume_from > 0) { 129413498266Sopenharmony_ci /* Let's read off the proper amount of bytes from the input. */ 129513498266Sopenharmony_ci if(conn->seek_func) { 129613498266Sopenharmony_ci Curl_set_in_callback(data, true); 129713498266Sopenharmony_ci seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, 129813498266Sopenharmony_ci SEEK_SET); 129913498266Sopenharmony_ci Curl_set_in_callback(data, false); 130013498266Sopenharmony_ci } 130113498266Sopenharmony_ci 130213498266Sopenharmony_ci if(seekerr != CURL_SEEKFUNC_OK) { 130313498266Sopenharmony_ci curl_off_t passed = 0; 130413498266Sopenharmony_ci 130513498266Sopenharmony_ci if(seekerr != CURL_SEEKFUNC_CANTSEEK) { 130613498266Sopenharmony_ci failf(data, "Could not seek stream"); 130713498266Sopenharmony_ci return CURLE_FTP_COULDNT_USE_REST; 130813498266Sopenharmony_ci } 130913498266Sopenharmony_ci /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */ 131013498266Sopenharmony_ci do { 131113498266Sopenharmony_ci char scratch[4*1024]; 131213498266Sopenharmony_ci size_t readthisamountnow = 131313498266Sopenharmony_ci (data->state.resume_from - passed > 131413498266Sopenharmony_ci (curl_off_t)sizeof(scratch)) ? 131513498266Sopenharmony_ci sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed); 131613498266Sopenharmony_ci 131713498266Sopenharmony_ci size_t actuallyread = 131813498266Sopenharmony_ci data->state.fread_func(scratch, 1, 131913498266Sopenharmony_ci readthisamountnow, data->state.in); 132013498266Sopenharmony_ci 132113498266Sopenharmony_ci passed += actuallyread; 132213498266Sopenharmony_ci if((actuallyread == 0) || (actuallyread > readthisamountnow)) { 132313498266Sopenharmony_ci /* this checks for greater-than only to make sure that the 132413498266Sopenharmony_ci CURL_READFUNC_ABORT return code still aborts */ 132513498266Sopenharmony_ci failf(data, "Failed to read data"); 132613498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST); 132713498266Sopenharmony_ci break; 132813498266Sopenharmony_ci } 132913498266Sopenharmony_ci } while(passed < data->state.resume_from); 133013498266Sopenharmony_ci if(rc) 133113498266Sopenharmony_ci break; 133213498266Sopenharmony_ci } 133313498266Sopenharmony_ci 133413498266Sopenharmony_ci /* now, decrease the size of the read */ 133513498266Sopenharmony_ci if(data->state.infilesize > 0) { 133613498266Sopenharmony_ci data->state.infilesize -= data->state.resume_from; 133713498266Sopenharmony_ci data->req.size = data->state.infilesize; 133813498266Sopenharmony_ci Curl_pgrsSetUploadSize(data, data->state.infilesize); 133913498266Sopenharmony_ci } 134013498266Sopenharmony_ci 134113498266Sopenharmony_ci rc = sftp_seek64(sshc->sftp_file, data->state.resume_from); 134213498266Sopenharmony_ci if(rc) { 134313498266Sopenharmony_ci MOVE_TO_SFTP_CLOSE_STATE(); 134413498266Sopenharmony_ci break; 134513498266Sopenharmony_ci } 134613498266Sopenharmony_ci } 134713498266Sopenharmony_ci if(data->state.infilesize > 0) { 134813498266Sopenharmony_ci data->req.size = data->state.infilesize; 134913498266Sopenharmony_ci Curl_pgrsSetUploadSize(data, data->state.infilesize); 135013498266Sopenharmony_ci } 135113498266Sopenharmony_ci /* upload data */ 135213498266Sopenharmony_ci Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET); 135313498266Sopenharmony_ci 135413498266Sopenharmony_ci /* not set by Curl_setup_transfer to preserve keepon bits */ 135513498266Sopenharmony_ci conn->sockfd = conn->writesockfd; 135613498266Sopenharmony_ci 135713498266Sopenharmony_ci /* store this original bitmask setup to use later on if we can't 135813498266Sopenharmony_ci figure out a "real" bitmask */ 135913498266Sopenharmony_ci sshc->orig_waitfor = data->req.keepon; 136013498266Sopenharmony_ci 136113498266Sopenharmony_ci /* we want to use the _sending_ function even when the socket turns 136213498266Sopenharmony_ci out readable as the underlying libssh sftp send function will deal 136313498266Sopenharmony_ci with both accordingly */ 136413498266Sopenharmony_ci data->state.select_bits = CURL_CSELECT_OUT; 136513498266Sopenharmony_ci 136613498266Sopenharmony_ci /* since we don't really wait for anything at this point, we want the 136713498266Sopenharmony_ci state machine to move on as soon as possible so we set a very short 136813498266Sopenharmony_ci timeout here */ 136913498266Sopenharmony_ci Curl_expire(data, 0, EXPIRE_RUN_NOW); 137013498266Sopenharmony_ci 137113498266Sopenharmony_ci state(data, SSH_STOP); 137213498266Sopenharmony_ci break; 137313498266Sopenharmony_ci } 137413498266Sopenharmony_ci 137513498266Sopenharmony_ci case SSH_SFTP_CREATE_DIRS_INIT: 137613498266Sopenharmony_ci if(strlen(protop->path) > 1) { 137713498266Sopenharmony_ci sshc->slash_pos = protop->path + 1; /* ignore the leading '/' */ 137813498266Sopenharmony_ci state(data, SSH_SFTP_CREATE_DIRS); 137913498266Sopenharmony_ci } 138013498266Sopenharmony_ci else { 138113498266Sopenharmony_ci state(data, SSH_SFTP_UPLOAD_INIT); 138213498266Sopenharmony_ci } 138313498266Sopenharmony_ci break; 138413498266Sopenharmony_ci 138513498266Sopenharmony_ci case SSH_SFTP_CREATE_DIRS: 138613498266Sopenharmony_ci sshc->slash_pos = strchr(sshc->slash_pos, '/'); 138713498266Sopenharmony_ci if(sshc->slash_pos) { 138813498266Sopenharmony_ci *sshc->slash_pos = 0; 138913498266Sopenharmony_ci 139013498266Sopenharmony_ci infof(data, "Creating directory '%s'", protop->path); 139113498266Sopenharmony_ci state(data, SSH_SFTP_CREATE_DIRS_MKDIR); 139213498266Sopenharmony_ci break; 139313498266Sopenharmony_ci } 139413498266Sopenharmony_ci state(data, SSH_SFTP_UPLOAD_INIT); 139513498266Sopenharmony_ci break; 139613498266Sopenharmony_ci 139713498266Sopenharmony_ci case SSH_SFTP_CREATE_DIRS_MKDIR: 139813498266Sopenharmony_ci /* 'mode' - parameter is preliminary - default to 0644 */ 139913498266Sopenharmony_ci rc = sftp_mkdir(sshc->sftp_session, protop->path, 140013498266Sopenharmony_ci (mode_t)data->set.new_directory_perms); 140113498266Sopenharmony_ci *sshc->slash_pos = '/'; 140213498266Sopenharmony_ci ++sshc->slash_pos; 140313498266Sopenharmony_ci if(rc < 0) { 140413498266Sopenharmony_ci /* 140513498266Sopenharmony_ci * Abort if failure wasn't that the dir already exists or the 140613498266Sopenharmony_ci * permission was denied (creation might succeed further down the 140713498266Sopenharmony_ci * path) - retry on unspecific FAILURE also 140813498266Sopenharmony_ci */ 140913498266Sopenharmony_ci err = sftp_get_error(sshc->sftp_session); 141013498266Sopenharmony_ci if((err != SSH_FX_FILE_ALREADY_EXISTS) && 141113498266Sopenharmony_ci (err != SSH_FX_FAILURE) && 141213498266Sopenharmony_ci (err != SSH_FX_PERMISSION_DENIED)) { 141313498266Sopenharmony_ci MOVE_TO_SFTP_CLOSE_STATE(); 141413498266Sopenharmony_ci break; 141513498266Sopenharmony_ci } 141613498266Sopenharmony_ci rc = 0; /* clear rc and continue */ 141713498266Sopenharmony_ci } 141813498266Sopenharmony_ci state(data, SSH_SFTP_CREATE_DIRS); 141913498266Sopenharmony_ci break; 142013498266Sopenharmony_ci 142113498266Sopenharmony_ci case SSH_SFTP_READDIR_INIT: 142213498266Sopenharmony_ci Curl_pgrsSetDownloadSize(data, -1); 142313498266Sopenharmony_ci if(data->req.no_body) { 142413498266Sopenharmony_ci state(data, SSH_STOP); 142513498266Sopenharmony_ci break; 142613498266Sopenharmony_ci } 142713498266Sopenharmony_ci 142813498266Sopenharmony_ci /* 142913498266Sopenharmony_ci * This is a directory that we are trying to get, so produce a directory 143013498266Sopenharmony_ci * listing 143113498266Sopenharmony_ci */ 143213498266Sopenharmony_ci sshc->sftp_dir = sftp_opendir(sshc->sftp_session, 143313498266Sopenharmony_ci protop->path); 143413498266Sopenharmony_ci if(!sshc->sftp_dir) { 143513498266Sopenharmony_ci failf(data, "Could not open directory for reading: %s", 143613498266Sopenharmony_ci ssh_get_error(sshc->ssh_session)); 143713498266Sopenharmony_ci MOVE_TO_SFTP_CLOSE_STATE(); 143813498266Sopenharmony_ci break; 143913498266Sopenharmony_ci } 144013498266Sopenharmony_ci state(data, SSH_SFTP_READDIR); 144113498266Sopenharmony_ci break; 144213498266Sopenharmony_ci 144313498266Sopenharmony_ci case SSH_SFTP_READDIR: 144413498266Sopenharmony_ci Curl_dyn_reset(&sshc->readdir_buf); 144513498266Sopenharmony_ci if(sshc->readdir_attrs) 144613498266Sopenharmony_ci sftp_attributes_free(sshc->readdir_attrs); 144713498266Sopenharmony_ci 144813498266Sopenharmony_ci sshc->readdir_attrs = sftp_readdir(sshc->sftp_session, sshc->sftp_dir); 144913498266Sopenharmony_ci if(sshc->readdir_attrs) { 145013498266Sopenharmony_ci sshc->readdir_filename = sshc->readdir_attrs->name; 145113498266Sopenharmony_ci sshc->readdir_longentry = sshc->readdir_attrs->longname; 145213498266Sopenharmony_ci sshc->readdir_len = strlen(sshc->readdir_filename); 145313498266Sopenharmony_ci 145413498266Sopenharmony_ci if(data->set.list_only) { 145513498266Sopenharmony_ci char *tmpLine; 145613498266Sopenharmony_ci 145713498266Sopenharmony_ci tmpLine = aprintf("%s\n", sshc->readdir_filename); 145813498266Sopenharmony_ci if(!tmpLine) { 145913498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 146013498266Sopenharmony_ci sshc->actualcode = CURLE_OUT_OF_MEMORY; 146113498266Sopenharmony_ci break; 146213498266Sopenharmony_ci } 146313498266Sopenharmony_ci result = Curl_client_write(data, CLIENTWRITE_BODY, 146413498266Sopenharmony_ci tmpLine, sshc->readdir_len + 1); 146513498266Sopenharmony_ci free(tmpLine); 146613498266Sopenharmony_ci 146713498266Sopenharmony_ci if(result) { 146813498266Sopenharmony_ci state(data, SSH_STOP); 146913498266Sopenharmony_ci break; 147013498266Sopenharmony_ci } 147113498266Sopenharmony_ci 147213498266Sopenharmony_ci } 147313498266Sopenharmony_ci else { 147413498266Sopenharmony_ci if(Curl_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) { 147513498266Sopenharmony_ci sshc->actualcode = CURLE_OUT_OF_MEMORY; 147613498266Sopenharmony_ci state(data, SSH_STOP); 147713498266Sopenharmony_ci break; 147813498266Sopenharmony_ci } 147913498266Sopenharmony_ci 148013498266Sopenharmony_ci if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) && 148113498266Sopenharmony_ci ((sshc->readdir_attrs->permissions & SSH_S_IFMT) == 148213498266Sopenharmony_ci SSH_S_IFLNK)) { 148313498266Sopenharmony_ci sshc->readdir_linkPath = aprintf("%s%s", protop->path, 148413498266Sopenharmony_ci sshc->readdir_filename); 148513498266Sopenharmony_ci 148613498266Sopenharmony_ci if(!sshc->readdir_linkPath) { 148713498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 148813498266Sopenharmony_ci sshc->actualcode = CURLE_OUT_OF_MEMORY; 148913498266Sopenharmony_ci break; 149013498266Sopenharmony_ci } 149113498266Sopenharmony_ci 149213498266Sopenharmony_ci state(data, SSH_SFTP_READDIR_LINK); 149313498266Sopenharmony_ci break; 149413498266Sopenharmony_ci } 149513498266Sopenharmony_ci state(data, SSH_SFTP_READDIR_BOTTOM); 149613498266Sopenharmony_ci break; 149713498266Sopenharmony_ci } 149813498266Sopenharmony_ci } 149913498266Sopenharmony_ci else if(sftp_dir_eof(sshc->sftp_dir)) { 150013498266Sopenharmony_ci state(data, SSH_SFTP_READDIR_DONE); 150113498266Sopenharmony_ci break; 150213498266Sopenharmony_ci } 150313498266Sopenharmony_ci else { 150413498266Sopenharmony_ci failf(data, "Could not open remote file for reading: %s", 150513498266Sopenharmony_ci ssh_get_error(sshc->ssh_session)); 150613498266Sopenharmony_ci MOVE_TO_SFTP_CLOSE_STATE(); 150713498266Sopenharmony_ci break; 150813498266Sopenharmony_ci } 150913498266Sopenharmony_ci break; 151013498266Sopenharmony_ci 151113498266Sopenharmony_ci case SSH_SFTP_READDIR_LINK: 151213498266Sopenharmony_ci if(sshc->readdir_link_attrs) 151313498266Sopenharmony_ci sftp_attributes_free(sshc->readdir_link_attrs); 151413498266Sopenharmony_ci 151513498266Sopenharmony_ci sshc->readdir_link_attrs = sftp_lstat(sshc->sftp_session, 151613498266Sopenharmony_ci sshc->readdir_linkPath); 151713498266Sopenharmony_ci if(sshc->readdir_link_attrs == 0) { 151813498266Sopenharmony_ci failf(data, "Could not read symlink for reading: %s", 151913498266Sopenharmony_ci ssh_get_error(sshc->ssh_session)); 152013498266Sopenharmony_ci MOVE_TO_SFTP_CLOSE_STATE(); 152113498266Sopenharmony_ci break; 152213498266Sopenharmony_ci } 152313498266Sopenharmony_ci 152413498266Sopenharmony_ci if(!sshc->readdir_link_attrs->name) { 152513498266Sopenharmony_ci sshc->readdir_tmp = sftp_readlink(sshc->sftp_session, 152613498266Sopenharmony_ci sshc->readdir_linkPath); 152713498266Sopenharmony_ci if(!sshc->readdir_filename) 152813498266Sopenharmony_ci sshc->readdir_len = 0; 152913498266Sopenharmony_ci else 153013498266Sopenharmony_ci sshc->readdir_len = strlen(sshc->readdir_tmp); 153113498266Sopenharmony_ci sshc->readdir_longentry = NULL; 153213498266Sopenharmony_ci sshc->readdir_filename = sshc->readdir_tmp; 153313498266Sopenharmony_ci } 153413498266Sopenharmony_ci else { 153513498266Sopenharmony_ci sshc->readdir_len = strlen(sshc->readdir_link_attrs->name); 153613498266Sopenharmony_ci sshc->readdir_filename = sshc->readdir_link_attrs->name; 153713498266Sopenharmony_ci sshc->readdir_longentry = sshc->readdir_link_attrs->longname; 153813498266Sopenharmony_ci } 153913498266Sopenharmony_ci 154013498266Sopenharmony_ci Curl_safefree(sshc->readdir_linkPath); 154113498266Sopenharmony_ci 154213498266Sopenharmony_ci if(Curl_dyn_addf(&sshc->readdir_buf, " -> %s", 154313498266Sopenharmony_ci sshc->readdir_filename)) { 154413498266Sopenharmony_ci sshc->actualcode = CURLE_OUT_OF_MEMORY; 154513498266Sopenharmony_ci break; 154613498266Sopenharmony_ci } 154713498266Sopenharmony_ci 154813498266Sopenharmony_ci sftp_attributes_free(sshc->readdir_link_attrs); 154913498266Sopenharmony_ci sshc->readdir_link_attrs = NULL; 155013498266Sopenharmony_ci sshc->readdir_filename = NULL; 155113498266Sopenharmony_ci sshc->readdir_longentry = NULL; 155213498266Sopenharmony_ci 155313498266Sopenharmony_ci state(data, SSH_SFTP_READDIR_BOTTOM); 155413498266Sopenharmony_ci FALLTHROUGH(); 155513498266Sopenharmony_ci case SSH_SFTP_READDIR_BOTTOM: 155613498266Sopenharmony_ci if(Curl_dyn_addn(&sshc->readdir_buf, "\n", 1)) 155713498266Sopenharmony_ci result = CURLE_OUT_OF_MEMORY; 155813498266Sopenharmony_ci else 155913498266Sopenharmony_ci result = Curl_client_write(data, CLIENTWRITE_BODY, 156013498266Sopenharmony_ci Curl_dyn_ptr(&sshc->readdir_buf), 156113498266Sopenharmony_ci Curl_dyn_len(&sshc->readdir_buf)); 156213498266Sopenharmony_ci 156313498266Sopenharmony_ci ssh_string_free_char(sshc->readdir_tmp); 156413498266Sopenharmony_ci sshc->readdir_tmp = NULL; 156513498266Sopenharmony_ci 156613498266Sopenharmony_ci if(result) { 156713498266Sopenharmony_ci state(data, SSH_STOP); 156813498266Sopenharmony_ci } 156913498266Sopenharmony_ci else 157013498266Sopenharmony_ci state(data, SSH_SFTP_READDIR); 157113498266Sopenharmony_ci break; 157213498266Sopenharmony_ci 157313498266Sopenharmony_ci case SSH_SFTP_READDIR_DONE: 157413498266Sopenharmony_ci sftp_closedir(sshc->sftp_dir); 157513498266Sopenharmony_ci sshc->sftp_dir = NULL; 157613498266Sopenharmony_ci 157713498266Sopenharmony_ci /* no data to transfer */ 157813498266Sopenharmony_ci Curl_setup_transfer(data, -1, -1, FALSE, -1); 157913498266Sopenharmony_ci state(data, SSH_STOP); 158013498266Sopenharmony_ci break; 158113498266Sopenharmony_ci 158213498266Sopenharmony_ci case SSH_SFTP_DOWNLOAD_INIT: 158313498266Sopenharmony_ci /* 158413498266Sopenharmony_ci * Work on getting the specified file 158513498266Sopenharmony_ci */ 158613498266Sopenharmony_ci if(sshc->sftp_file) 158713498266Sopenharmony_ci sftp_close(sshc->sftp_file); 158813498266Sopenharmony_ci 158913498266Sopenharmony_ci sshc->sftp_file = sftp_open(sshc->sftp_session, protop->path, 159013498266Sopenharmony_ci O_RDONLY, (mode_t)data->set.new_file_perms); 159113498266Sopenharmony_ci if(!sshc->sftp_file) { 159213498266Sopenharmony_ci failf(data, "Could not open remote file for reading: %s", 159313498266Sopenharmony_ci ssh_get_error(sshc->ssh_session)); 159413498266Sopenharmony_ci 159513498266Sopenharmony_ci MOVE_TO_SFTP_CLOSE_STATE(); 159613498266Sopenharmony_ci break; 159713498266Sopenharmony_ci } 159813498266Sopenharmony_ci sftp_file_set_nonblocking(sshc->sftp_file); 159913498266Sopenharmony_ci state(data, SSH_SFTP_DOWNLOAD_STAT); 160013498266Sopenharmony_ci break; 160113498266Sopenharmony_ci 160213498266Sopenharmony_ci case SSH_SFTP_DOWNLOAD_STAT: 160313498266Sopenharmony_ci { 160413498266Sopenharmony_ci sftp_attributes attrs; 160513498266Sopenharmony_ci curl_off_t size; 160613498266Sopenharmony_ci 160713498266Sopenharmony_ci attrs = sftp_fstat(sshc->sftp_file); 160813498266Sopenharmony_ci if(!attrs || 160913498266Sopenharmony_ci !(attrs->flags & SSH_FILEXFER_ATTR_SIZE) || 161013498266Sopenharmony_ci (attrs->size == 0)) { 161113498266Sopenharmony_ci /* 161213498266Sopenharmony_ci * sftp_fstat didn't return an error, so maybe the server 161313498266Sopenharmony_ci * just doesn't support stat() 161413498266Sopenharmony_ci * OR the server doesn't return a file size with a stat() 161513498266Sopenharmony_ci * OR file size is 0 161613498266Sopenharmony_ci */ 161713498266Sopenharmony_ci data->req.size = -1; 161813498266Sopenharmony_ci data->req.maxdownload = -1; 161913498266Sopenharmony_ci Curl_pgrsSetDownloadSize(data, -1); 162013498266Sopenharmony_ci size = 0; 162113498266Sopenharmony_ci } 162213498266Sopenharmony_ci else { 162313498266Sopenharmony_ci size = attrs->size; 162413498266Sopenharmony_ci 162513498266Sopenharmony_ci sftp_attributes_free(attrs); 162613498266Sopenharmony_ci 162713498266Sopenharmony_ci if(size < 0) { 162813498266Sopenharmony_ci failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); 162913498266Sopenharmony_ci return CURLE_BAD_DOWNLOAD_RESUME; 163013498266Sopenharmony_ci } 163113498266Sopenharmony_ci if(data->state.use_range) { 163213498266Sopenharmony_ci curl_off_t from, to; 163313498266Sopenharmony_ci char *ptr; 163413498266Sopenharmony_ci char *ptr2; 163513498266Sopenharmony_ci CURLofft to_t; 163613498266Sopenharmony_ci CURLofft from_t; 163713498266Sopenharmony_ci 163813498266Sopenharmony_ci from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from); 163913498266Sopenharmony_ci if(from_t == CURL_OFFT_FLOW) { 164013498266Sopenharmony_ci return CURLE_RANGE_ERROR; 164113498266Sopenharmony_ci } 164213498266Sopenharmony_ci while(*ptr && (ISBLANK(*ptr) || (*ptr == '-'))) 164313498266Sopenharmony_ci ptr++; 164413498266Sopenharmony_ci to_t = curlx_strtoofft(ptr, &ptr2, 10, &to); 164513498266Sopenharmony_ci if(to_t == CURL_OFFT_FLOW) { 164613498266Sopenharmony_ci return CURLE_RANGE_ERROR; 164713498266Sopenharmony_ci } 164813498266Sopenharmony_ci if((to_t == CURL_OFFT_INVAL) /* no "to" value given */ 164913498266Sopenharmony_ci || (to >= size)) { 165013498266Sopenharmony_ci to = size - 1; 165113498266Sopenharmony_ci } 165213498266Sopenharmony_ci if(from_t) { 165313498266Sopenharmony_ci /* from is relative to end of file */ 165413498266Sopenharmony_ci from = size - to; 165513498266Sopenharmony_ci to = size - 1; 165613498266Sopenharmony_ci } 165713498266Sopenharmony_ci if(from > size) { 165813498266Sopenharmony_ci failf(data, "Offset (%" 165913498266Sopenharmony_ci CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" 166013498266Sopenharmony_ci CURL_FORMAT_CURL_OFF_T ")", from, size); 166113498266Sopenharmony_ci return CURLE_BAD_DOWNLOAD_RESUME; 166213498266Sopenharmony_ci } 166313498266Sopenharmony_ci if(from > to) { 166413498266Sopenharmony_ci from = to; 166513498266Sopenharmony_ci size = 0; 166613498266Sopenharmony_ci } 166713498266Sopenharmony_ci else { 166813498266Sopenharmony_ci size = to - from + 1; 166913498266Sopenharmony_ci } 167013498266Sopenharmony_ci 167113498266Sopenharmony_ci rc = sftp_seek64(sshc->sftp_file, from); 167213498266Sopenharmony_ci if(rc) { 167313498266Sopenharmony_ci MOVE_TO_SFTP_CLOSE_STATE(); 167413498266Sopenharmony_ci break; 167513498266Sopenharmony_ci } 167613498266Sopenharmony_ci } 167713498266Sopenharmony_ci data->req.size = size; 167813498266Sopenharmony_ci data->req.maxdownload = size; 167913498266Sopenharmony_ci Curl_pgrsSetDownloadSize(data, size); 168013498266Sopenharmony_ci } 168113498266Sopenharmony_ci 168213498266Sopenharmony_ci /* We can resume if we can seek to the resume position */ 168313498266Sopenharmony_ci if(data->state.resume_from) { 168413498266Sopenharmony_ci if(data->state.resume_from < 0) { 168513498266Sopenharmony_ci /* We're supposed to download the last abs(from) bytes */ 168613498266Sopenharmony_ci if((curl_off_t)size < -data->state.resume_from) { 168713498266Sopenharmony_ci failf(data, "Offset (%" 168813498266Sopenharmony_ci CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" 168913498266Sopenharmony_ci CURL_FORMAT_CURL_OFF_T ")", 169013498266Sopenharmony_ci data->state.resume_from, size); 169113498266Sopenharmony_ci return CURLE_BAD_DOWNLOAD_RESUME; 169213498266Sopenharmony_ci } 169313498266Sopenharmony_ci /* download from where? */ 169413498266Sopenharmony_ci data->state.resume_from += size; 169513498266Sopenharmony_ci } 169613498266Sopenharmony_ci else { 169713498266Sopenharmony_ci if((curl_off_t)size < data->state.resume_from) { 169813498266Sopenharmony_ci failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T 169913498266Sopenharmony_ci ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", 170013498266Sopenharmony_ci data->state.resume_from, size); 170113498266Sopenharmony_ci return CURLE_BAD_DOWNLOAD_RESUME; 170213498266Sopenharmony_ci } 170313498266Sopenharmony_ci } 170413498266Sopenharmony_ci /* Now store the number of bytes we are expected to download */ 170513498266Sopenharmony_ci data->req.size = size - data->state.resume_from; 170613498266Sopenharmony_ci data->req.maxdownload = size - data->state.resume_from; 170713498266Sopenharmony_ci Curl_pgrsSetDownloadSize(data, 170813498266Sopenharmony_ci size - data->state.resume_from); 170913498266Sopenharmony_ci 171013498266Sopenharmony_ci rc = sftp_seek64(sshc->sftp_file, data->state.resume_from); 171113498266Sopenharmony_ci if(rc) { 171213498266Sopenharmony_ci MOVE_TO_SFTP_CLOSE_STATE(); 171313498266Sopenharmony_ci break; 171413498266Sopenharmony_ci } 171513498266Sopenharmony_ci } 171613498266Sopenharmony_ci } 171713498266Sopenharmony_ci 171813498266Sopenharmony_ci /* Setup the actual download */ 171913498266Sopenharmony_ci if(data->req.size == 0) { 172013498266Sopenharmony_ci /* no data to transfer */ 172113498266Sopenharmony_ci Curl_setup_transfer(data, -1, -1, FALSE, -1); 172213498266Sopenharmony_ci infof(data, "File already completely downloaded"); 172313498266Sopenharmony_ci state(data, SSH_STOP); 172413498266Sopenharmony_ci break; 172513498266Sopenharmony_ci } 172613498266Sopenharmony_ci Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1); 172713498266Sopenharmony_ci 172813498266Sopenharmony_ci /* not set by Curl_setup_transfer to preserve keepon bits */ 172913498266Sopenharmony_ci conn->writesockfd = conn->sockfd; 173013498266Sopenharmony_ci 173113498266Sopenharmony_ci /* we want to use the _receiving_ function even when the socket turns 173213498266Sopenharmony_ci out writableable as the underlying libssh recv function will deal 173313498266Sopenharmony_ci with both accordingly */ 173413498266Sopenharmony_ci data->state.select_bits = CURL_CSELECT_IN; 173513498266Sopenharmony_ci 173613498266Sopenharmony_ci if(result) { 173713498266Sopenharmony_ci /* this should never occur; the close state should be entered 173813498266Sopenharmony_ci at the time the error occurs */ 173913498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 174013498266Sopenharmony_ci sshc->actualcode = result; 174113498266Sopenharmony_ci } 174213498266Sopenharmony_ci else { 174313498266Sopenharmony_ci sshc->sftp_recv_state = 0; 174413498266Sopenharmony_ci state(data, SSH_STOP); 174513498266Sopenharmony_ci } 174613498266Sopenharmony_ci break; 174713498266Sopenharmony_ci 174813498266Sopenharmony_ci case SSH_SFTP_CLOSE: 174913498266Sopenharmony_ci if(sshc->sftp_file) { 175013498266Sopenharmony_ci sftp_close(sshc->sftp_file); 175113498266Sopenharmony_ci sshc->sftp_file = NULL; 175213498266Sopenharmony_ci } 175313498266Sopenharmony_ci Curl_safefree(protop->path); 175413498266Sopenharmony_ci 175513498266Sopenharmony_ci DEBUGF(infof(data, "SFTP DONE done")); 175613498266Sopenharmony_ci 175713498266Sopenharmony_ci /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT 175813498266Sopenharmony_ci After nextstate is executed, the control should come back to 175913498266Sopenharmony_ci SSH_SFTP_CLOSE to pass the correct result back */ 176013498266Sopenharmony_ci if(sshc->nextstate != SSH_NO_STATE && 176113498266Sopenharmony_ci sshc->nextstate != SSH_SFTP_CLOSE) { 176213498266Sopenharmony_ci state(data, sshc->nextstate); 176313498266Sopenharmony_ci sshc->nextstate = SSH_SFTP_CLOSE; 176413498266Sopenharmony_ci } 176513498266Sopenharmony_ci else { 176613498266Sopenharmony_ci state(data, SSH_STOP); 176713498266Sopenharmony_ci result = sshc->actualcode; 176813498266Sopenharmony_ci } 176913498266Sopenharmony_ci break; 177013498266Sopenharmony_ci 177113498266Sopenharmony_ci case SSH_SFTP_SHUTDOWN: 177213498266Sopenharmony_ci /* during times we get here due to a broken transfer and then the 177313498266Sopenharmony_ci sftp_handle might not have been taken down so make sure that is done 177413498266Sopenharmony_ci before we proceed */ 177513498266Sopenharmony_ci 177613498266Sopenharmony_ci if(sshc->sftp_file) { 177713498266Sopenharmony_ci sftp_close(sshc->sftp_file); 177813498266Sopenharmony_ci sshc->sftp_file = NULL; 177913498266Sopenharmony_ci } 178013498266Sopenharmony_ci 178113498266Sopenharmony_ci if(sshc->sftp_session) { 178213498266Sopenharmony_ci sftp_free(sshc->sftp_session); 178313498266Sopenharmony_ci sshc->sftp_session = NULL; 178413498266Sopenharmony_ci } 178513498266Sopenharmony_ci 178613498266Sopenharmony_ci SSH_STRING_FREE_CHAR(sshc->homedir); 178713498266Sopenharmony_ci data->state.most_recent_ftp_entrypath = NULL; 178813498266Sopenharmony_ci 178913498266Sopenharmony_ci state(data, SSH_SESSION_DISCONNECT); 179013498266Sopenharmony_ci break; 179113498266Sopenharmony_ci 179213498266Sopenharmony_ci case SSH_SCP_TRANS_INIT: 179313498266Sopenharmony_ci result = Curl_getworkingpath(data, sshc->homedir, &protop->path); 179413498266Sopenharmony_ci if(result) { 179513498266Sopenharmony_ci sshc->actualcode = result; 179613498266Sopenharmony_ci state(data, SSH_STOP); 179713498266Sopenharmony_ci break; 179813498266Sopenharmony_ci } 179913498266Sopenharmony_ci 180013498266Sopenharmony_ci /* Functions from the SCP subsystem cannot handle/return SSH_AGAIN */ 180113498266Sopenharmony_ci ssh_set_blocking(sshc->ssh_session, 1); 180213498266Sopenharmony_ci 180313498266Sopenharmony_ci if(data->state.upload) { 180413498266Sopenharmony_ci if(data->state.infilesize < 0) { 180513498266Sopenharmony_ci failf(data, "SCP requires a known file size for upload"); 180613498266Sopenharmony_ci sshc->actualcode = CURLE_UPLOAD_FAILED; 180713498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); 180813498266Sopenharmony_ci break; 180913498266Sopenharmony_ci } 181013498266Sopenharmony_ci 181113498266Sopenharmony_ci sshc->scp_session = 181213498266Sopenharmony_ci ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, protop->path); 181313498266Sopenharmony_ci state(data, SSH_SCP_UPLOAD_INIT); 181413498266Sopenharmony_ci } 181513498266Sopenharmony_ci else { 181613498266Sopenharmony_ci sshc->scp_session = 181713498266Sopenharmony_ci ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, protop->path); 181813498266Sopenharmony_ci state(data, SSH_SCP_DOWNLOAD_INIT); 181913498266Sopenharmony_ci } 182013498266Sopenharmony_ci 182113498266Sopenharmony_ci if(!sshc->scp_session) { 182213498266Sopenharmony_ci err_msg = ssh_get_error(sshc->ssh_session); 182313498266Sopenharmony_ci failf(data, "%s", err_msg); 182413498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); 182513498266Sopenharmony_ci } 182613498266Sopenharmony_ci 182713498266Sopenharmony_ci break; 182813498266Sopenharmony_ci 182913498266Sopenharmony_ci case SSH_SCP_UPLOAD_INIT: 183013498266Sopenharmony_ci 183113498266Sopenharmony_ci rc = ssh_scp_init(sshc->scp_session); 183213498266Sopenharmony_ci if(rc != SSH_OK) { 183313498266Sopenharmony_ci err_msg = ssh_get_error(sshc->ssh_session); 183413498266Sopenharmony_ci failf(data, "%s", err_msg); 183513498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); 183613498266Sopenharmony_ci break; 183713498266Sopenharmony_ci } 183813498266Sopenharmony_ci 183913498266Sopenharmony_ci rc = ssh_scp_push_file(sshc->scp_session, protop->path, 184013498266Sopenharmony_ci data->state.infilesize, 184113498266Sopenharmony_ci (int)data->set.new_file_perms); 184213498266Sopenharmony_ci if(rc != SSH_OK) { 184313498266Sopenharmony_ci err_msg = ssh_get_error(sshc->ssh_session); 184413498266Sopenharmony_ci failf(data, "%s", err_msg); 184513498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED); 184613498266Sopenharmony_ci break; 184713498266Sopenharmony_ci } 184813498266Sopenharmony_ci 184913498266Sopenharmony_ci /* upload data */ 185013498266Sopenharmony_ci Curl_setup_transfer(data, -1, data->req.size, FALSE, FIRSTSOCKET); 185113498266Sopenharmony_ci 185213498266Sopenharmony_ci /* not set by Curl_setup_transfer to preserve keepon bits */ 185313498266Sopenharmony_ci conn->sockfd = conn->writesockfd; 185413498266Sopenharmony_ci 185513498266Sopenharmony_ci /* store this original bitmask setup to use later on if we can't 185613498266Sopenharmony_ci figure out a "real" bitmask */ 185713498266Sopenharmony_ci sshc->orig_waitfor = data->req.keepon; 185813498266Sopenharmony_ci 185913498266Sopenharmony_ci /* we want to use the _sending_ function even when the socket turns 186013498266Sopenharmony_ci out readable as the underlying libssh scp send function will deal 186113498266Sopenharmony_ci with both accordingly */ 186213498266Sopenharmony_ci data->state.select_bits = CURL_CSELECT_OUT; 186313498266Sopenharmony_ci 186413498266Sopenharmony_ci state(data, SSH_STOP); 186513498266Sopenharmony_ci 186613498266Sopenharmony_ci break; 186713498266Sopenharmony_ci 186813498266Sopenharmony_ci case SSH_SCP_DOWNLOAD_INIT: 186913498266Sopenharmony_ci 187013498266Sopenharmony_ci rc = ssh_scp_init(sshc->scp_session); 187113498266Sopenharmony_ci if(rc != SSH_OK) { 187213498266Sopenharmony_ci err_msg = ssh_get_error(sshc->ssh_session); 187313498266Sopenharmony_ci failf(data, "%s", err_msg); 187413498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT); 187513498266Sopenharmony_ci break; 187613498266Sopenharmony_ci } 187713498266Sopenharmony_ci state(data, SSH_SCP_DOWNLOAD); 187813498266Sopenharmony_ci FALLTHROUGH(); 187913498266Sopenharmony_ci 188013498266Sopenharmony_ci case SSH_SCP_DOWNLOAD:{ 188113498266Sopenharmony_ci curl_off_t bytecount; 188213498266Sopenharmony_ci 188313498266Sopenharmony_ci rc = ssh_scp_pull_request(sshc->scp_session); 188413498266Sopenharmony_ci if(rc != SSH_SCP_REQUEST_NEWFILE) { 188513498266Sopenharmony_ci err_msg = ssh_get_error(sshc->ssh_session); 188613498266Sopenharmony_ci failf(data, "%s", err_msg); 188713498266Sopenharmony_ci MOVE_TO_ERROR_STATE(CURLE_REMOTE_FILE_NOT_FOUND); 188813498266Sopenharmony_ci break; 188913498266Sopenharmony_ci } 189013498266Sopenharmony_ci 189113498266Sopenharmony_ci /* download data */ 189213498266Sopenharmony_ci bytecount = ssh_scp_request_get_size(sshc->scp_session); 189313498266Sopenharmony_ci data->req.maxdownload = (curl_off_t) bytecount; 189413498266Sopenharmony_ci Curl_setup_transfer(data, FIRSTSOCKET, bytecount, FALSE, -1); 189513498266Sopenharmony_ci 189613498266Sopenharmony_ci /* not set by Curl_setup_transfer to preserve keepon bits */ 189713498266Sopenharmony_ci conn->writesockfd = conn->sockfd; 189813498266Sopenharmony_ci 189913498266Sopenharmony_ci /* we want to use the _receiving_ function even when the socket turns 190013498266Sopenharmony_ci out writableable as the underlying libssh recv function will deal 190113498266Sopenharmony_ci with both accordingly */ 190213498266Sopenharmony_ci data->state.select_bits = CURL_CSELECT_IN; 190313498266Sopenharmony_ci 190413498266Sopenharmony_ci state(data, SSH_STOP); 190513498266Sopenharmony_ci break; 190613498266Sopenharmony_ci } 190713498266Sopenharmony_ci case SSH_SCP_DONE: 190813498266Sopenharmony_ci if(data->state.upload) 190913498266Sopenharmony_ci state(data, SSH_SCP_SEND_EOF); 191013498266Sopenharmony_ci else 191113498266Sopenharmony_ci state(data, SSH_SCP_CHANNEL_FREE); 191213498266Sopenharmony_ci break; 191313498266Sopenharmony_ci 191413498266Sopenharmony_ci case SSH_SCP_SEND_EOF: 191513498266Sopenharmony_ci if(sshc->scp_session) { 191613498266Sopenharmony_ci rc = ssh_scp_close(sshc->scp_session); 191713498266Sopenharmony_ci if(rc == SSH_AGAIN) { 191813498266Sopenharmony_ci /* Currently the ssh_scp_close handles waiting for EOF in 191913498266Sopenharmony_ci * blocking way. 192013498266Sopenharmony_ci */ 192113498266Sopenharmony_ci break; 192213498266Sopenharmony_ci } 192313498266Sopenharmony_ci if(rc != SSH_OK) { 192413498266Sopenharmony_ci infof(data, "Failed to close libssh scp channel: %s", 192513498266Sopenharmony_ci ssh_get_error(sshc->ssh_session)); 192613498266Sopenharmony_ci } 192713498266Sopenharmony_ci } 192813498266Sopenharmony_ci 192913498266Sopenharmony_ci state(data, SSH_SCP_CHANNEL_FREE); 193013498266Sopenharmony_ci break; 193113498266Sopenharmony_ci 193213498266Sopenharmony_ci case SSH_SCP_CHANNEL_FREE: 193313498266Sopenharmony_ci if(sshc->scp_session) { 193413498266Sopenharmony_ci ssh_scp_free(sshc->scp_session); 193513498266Sopenharmony_ci sshc->scp_session = NULL; 193613498266Sopenharmony_ci } 193713498266Sopenharmony_ci DEBUGF(infof(data, "SCP DONE phase complete")); 193813498266Sopenharmony_ci 193913498266Sopenharmony_ci ssh_set_blocking(sshc->ssh_session, 0); 194013498266Sopenharmony_ci 194113498266Sopenharmony_ci state(data, SSH_SESSION_DISCONNECT); 194213498266Sopenharmony_ci FALLTHROUGH(); 194313498266Sopenharmony_ci 194413498266Sopenharmony_ci case SSH_SESSION_DISCONNECT: 194513498266Sopenharmony_ci /* during weird times when we've been prematurely aborted, the channel 194613498266Sopenharmony_ci is still alive when we reach this state and we MUST kill the channel 194713498266Sopenharmony_ci properly first */ 194813498266Sopenharmony_ci if(sshc->scp_session) { 194913498266Sopenharmony_ci ssh_scp_free(sshc->scp_session); 195013498266Sopenharmony_ci sshc->scp_session = NULL; 195113498266Sopenharmony_ci } 195213498266Sopenharmony_ci 195313498266Sopenharmony_ci ssh_disconnect(sshc->ssh_session); 195413498266Sopenharmony_ci if(!ssh_version(SSH_VERSION_INT(0, 10, 0))) { 195513498266Sopenharmony_ci /* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back, 195613498266Sopenharmony_ci tell the connection to forget about it. This libssh 195713498266Sopenharmony_ci bug is fixed in 0.10.0. */ 195813498266Sopenharmony_ci Curl_conn_forget_socket(data, FIRSTSOCKET); 195913498266Sopenharmony_ci } 196013498266Sopenharmony_ci 196113498266Sopenharmony_ci SSH_STRING_FREE_CHAR(sshc->homedir); 196213498266Sopenharmony_ci data->state.most_recent_ftp_entrypath = NULL; 196313498266Sopenharmony_ci 196413498266Sopenharmony_ci state(data, SSH_SESSION_FREE); 196513498266Sopenharmony_ci FALLTHROUGH(); 196613498266Sopenharmony_ci case SSH_SESSION_FREE: 196713498266Sopenharmony_ci if(sshc->ssh_session) { 196813498266Sopenharmony_ci ssh_free(sshc->ssh_session); 196913498266Sopenharmony_ci sshc->ssh_session = NULL; 197013498266Sopenharmony_ci } 197113498266Sopenharmony_ci 197213498266Sopenharmony_ci /* worst-case scenario cleanup */ 197313498266Sopenharmony_ci 197413498266Sopenharmony_ci DEBUGASSERT(sshc->ssh_session == NULL); 197513498266Sopenharmony_ci DEBUGASSERT(sshc->scp_session == NULL); 197613498266Sopenharmony_ci 197713498266Sopenharmony_ci if(sshc->readdir_tmp) { 197813498266Sopenharmony_ci ssh_string_free_char(sshc->readdir_tmp); 197913498266Sopenharmony_ci sshc->readdir_tmp = NULL; 198013498266Sopenharmony_ci } 198113498266Sopenharmony_ci 198213498266Sopenharmony_ci if(sshc->quote_attrs) 198313498266Sopenharmony_ci sftp_attributes_free(sshc->quote_attrs); 198413498266Sopenharmony_ci 198513498266Sopenharmony_ci if(sshc->readdir_attrs) 198613498266Sopenharmony_ci sftp_attributes_free(sshc->readdir_attrs); 198713498266Sopenharmony_ci 198813498266Sopenharmony_ci if(sshc->readdir_link_attrs) 198913498266Sopenharmony_ci sftp_attributes_free(sshc->readdir_link_attrs); 199013498266Sopenharmony_ci 199113498266Sopenharmony_ci if(sshc->privkey) 199213498266Sopenharmony_ci ssh_key_free(sshc->privkey); 199313498266Sopenharmony_ci if(sshc->pubkey) 199413498266Sopenharmony_ci ssh_key_free(sshc->pubkey); 199513498266Sopenharmony_ci 199613498266Sopenharmony_ci Curl_safefree(sshc->rsa_pub); 199713498266Sopenharmony_ci Curl_safefree(sshc->rsa); 199813498266Sopenharmony_ci Curl_safefree(sshc->quote_path1); 199913498266Sopenharmony_ci Curl_safefree(sshc->quote_path2); 200013498266Sopenharmony_ci Curl_dyn_free(&sshc->readdir_buf); 200113498266Sopenharmony_ci Curl_safefree(sshc->readdir_linkPath); 200213498266Sopenharmony_ci SSH_STRING_FREE_CHAR(sshc->homedir); 200313498266Sopenharmony_ci 200413498266Sopenharmony_ci /* the code we are about to return */ 200513498266Sopenharmony_ci result = sshc->actualcode; 200613498266Sopenharmony_ci 200713498266Sopenharmony_ci memset(sshc, 0, sizeof(struct ssh_conn)); 200813498266Sopenharmony_ci 200913498266Sopenharmony_ci connclose(conn, "SSH session free"); 201013498266Sopenharmony_ci sshc->state = SSH_SESSION_FREE; /* current */ 201113498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 201213498266Sopenharmony_ci state(data, SSH_STOP); 201313498266Sopenharmony_ci break; 201413498266Sopenharmony_ci 201513498266Sopenharmony_ci case SSH_QUIT: 201613498266Sopenharmony_ci default: 201713498266Sopenharmony_ci /* internal error */ 201813498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 201913498266Sopenharmony_ci state(data, SSH_STOP); 202013498266Sopenharmony_ci break; 202113498266Sopenharmony_ci 202213498266Sopenharmony_ci } 202313498266Sopenharmony_ci } while(!rc && (sshc->state != SSH_STOP)); 202413498266Sopenharmony_ci 202513498266Sopenharmony_ci 202613498266Sopenharmony_ci if(rc == SSH_AGAIN) { 202713498266Sopenharmony_ci /* we would block, we need to wait for the socket to be ready (in the 202813498266Sopenharmony_ci right direction too)! */ 202913498266Sopenharmony_ci *block = TRUE; 203013498266Sopenharmony_ci } 203113498266Sopenharmony_ci 203213498266Sopenharmony_ci return result; 203313498266Sopenharmony_ci} 203413498266Sopenharmony_ci 203513498266Sopenharmony_ci 203613498266Sopenharmony_ci/* called by the multi interface to figure out what socket(s) to wait for and 203713498266Sopenharmony_ci for what actions in the DO_DONE, PERFORM and WAITPERFORM states */ 203813498266Sopenharmony_cistatic int myssh_getsock(struct Curl_easy *data, 203913498266Sopenharmony_ci struct connectdata *conn, 204013498266Sopenharmony_ci curl_socket_t *sock) 204113498266Sopenharmony_ci{ 204213498266Sopenharmony_ci int bitmap = GETSOCK_BLANK; 204313498266Sopenharmony_ci (void)data; 204413498266Sopenharmony_ci sock[0] = conn->sock[FIRSTSOCKET]; 204513498266Sopenharmony_ci 204613498266Sopenharmony_ci if(conn->waitfor & KEEP_RECV) 204713498266Sopenharmony_ci bitmap |= GETSOCK_READSOCK(FIRSTSOCKET); 204813498266Sopenharmony_ci 204913498266Sopenharmony_ci if(conn->waitfor & KEEP_SEND) 205013498266Sopenharmony_ci bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); 205113498266Sopenharmony_ci 205213498266Sopenharmony_ci if(!conn->waitfor) 205313498266Sopenharmony_ci bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET); 205413498266Sopenharmony_ci 205513498266Sopenharmony_ci return bitmap; 205613498266Sopenharmony_ci} 205713498266Sopenharmony_ci 205813498266Sopenharmony_cistatic void myssh_block2waitfor(struct connectdata *conn, bool block) 205913498266Sopenharmony_ci{ 206013498266Sopenharmony_ci struct ssh_conn *sshc = &conn->proto.sshc; 206113498266Sopenharmony_ci 206213498266Sopenharmony_ci /* If it didn't block, or nothing was returned by ssh_get_poll_flags 206313498266Sopenharmony_ci * have the original set */ 206413498266Sopenharmony_ci conn->waitfor = sshc->orig_waitfor; 206513498266Sopenharmony_ci 206613498266Sopenharmony_ci if(block) { 206713498266Sopenharmony_ci int dir = ssh_get_poll_flags(sshc->ssh_session); 206813498266Sopenharmony_ci if(dir & SSH_READ_PENDING) { 206913498266Sopenharmony_ci /* translate the libssh define bits into our own bit defines */ 207013498266Sopenharmony_ci conn->waitfor = KEEP_RECV; 207113498266Sopenharmony_ci } 207213498266Sopenharmony_ci else if(dir & SSH_WRITE_PENDING) { 207313498266Sopenharmony_ci conn->waitfor = KEEP_SEND; 207413498266Sopenharmony_ci } 207513498266Sopenharmony_ci } 207613498266Sopenharmony_ci} 207713498266Sopenharmony_ci 207813498266Sopenharmony_ci/* called repeatedly until done from multi.c */ 207913498266Sopenharmony_cistatic CURLcode myssh_multi_statemach(struct Curl_easy *data, 208013498266Sopenharmony_ci bool *done) 208113498266Sopenharmony_ci{ 208213498266Sopenharmony_ci struct connectdata *conn = data->conn; 208313498266Sopenharmony_ci struct ssh_conn *sshc = &conn->proto.sshc; 208413498266Sopenharmony_ci bool block; /* we store the status and use that to provide a ssh_getsock() 208513498266Sopenharmony_ci implementation */ 208613498266Sopenharmony_ci CURLcode result = myssh_statemach_act(data, &block); 208713498266Sopenharmony_ci 208813498266Sopenharmony_ci *done = (sshc->state == SSH_STOP) ? TRUE : FALSE; 208913498266Sopenharmony_ci myssh_block2waitfor(conn, block); 209013498266Sopenharmony_ci 209113498266Sopenharmony_ci return result; 209213498266Sopenharmony_ci} 209313498266Sopenharmony_ci 209413498266Sopenharmony_cistatic CURLcode myssh_block_statemach(struct Curl_easy *data, 209513498266Sopenharmony_ci bool disconnect) 209613498266Sopenharmony_ci{ 209713498266Sopenharmony_ci struct connectdata *conn = data->conn; 209813498266Sopenharmony_ci struct ssh_conn *sshc = &conn->proto.sshc; 209913498266Sopenharmony_ci CURLcode result = CURLE_OK; 210013498266Sopenharmony_ci 210113498266Sopenharmony_ci while((sshc->state != SSH_STOP) && !result) { 210213498266Sopenharmony_ci bool block; 210313498266Sopenharmony_ci timediff_t left = 1000; 210413498266Sopenharmony_ci struct curltime now = Curl_now(); 210513498266Sopenharmony_ci 210613498266Sopenharmony_ci result = myssh_statemach_act(data, &block); 210713498266Sopenharmony_ci if(result) 210813498266Sopenharmony_ci break; 210913498266Sopenharmony_ci 211013498266Sopenharmony_ci if(!disconnect) { 211113498266Sopenharmony_ci if(Curl_pgrsUpdate(data)) 211213498266Sopenharmony_ci return CURLE_ABORTED_BY_CALLBACK; 211313498266Sopenharmony_ci 211413498266Sopenharmony_ci result = Curl_speedcheck(data, now); 211513498266Sopenharmony_ci if(result) 211613498266Sopenharmony_ci break; 211713498266Sopenharmony_ci 211813498266Sopenharmony_ci left = Curl_timeleft(data, NULL, FALSE); 211913498266Sopenharmony_ci if(left < 0) { 212013498266Sopenharmony_ci failf(data, "Operation timed out"); 212113498266Sopenharmony_ci return CURLE_OPERATION_TIMEDOUT; 212213498266Sopenharmony_ci } 212313498266Sopenharmony_ci } 212413498266Sopenharmony_ci 212513498266Sopenharmony_ci if(block) { 212613498266Sopenharmony_ci curl_socket_t fd_read = conn->sock[FIRSTSOCKET]; 212713498266Sopenharmony_ci /* wait for the socket to become ready */ 212813498266Sopenharmony_ci (void) Curl_socket_check(fd_read, CURL_SOCKET_BAD, 212913498266Sopenharmony_ci CURL_SOCKET_BAD, left > 1000 ? 1000 : left); 213013498266Sopenharmony_ci } 213113498266Sopenharmony_ci 213213498266Sopenharmony_ci } 213313498266Sopenharmony_ci 213413498266Sopenharmony_ci return result; 213513498266Sopenharmony_ci} 213613498266Sopenharmony_ci 213713498266Sopenharmony_ci/* 213813498266Sopenharmony_ci * SSH setup connection 213913498266Sopenharmony_ci */ 214013498266Sopenharmony_cistatic CURLcode myssh_setup_connection(struct Curl_easy *data, 214113498266Sopenharmony_ci struct connectdata *conn) 214213498266Sopenharmony_ci{ 214313498266Sopenharmony_ci struct SSHPROTO *ssh; 214413498266Sopenharmony_ci struct ssh_conn *sshc = &conn->proto.sshc; 214513498266Sopenharmony_ci 214613498266Sopenharmony_ci data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO)); 214713498266Sopenharmony_ci if(!ssh) 214813498266Sopenharmony_ci return CURLE_OUT_OF_MEMORY; 214913498266Sopenharmony_ci Curl_dyn_init(&sshc->readdir_buf, PATH_MAX * 2); 215013498266Sopenharmony_ci 215113498266Sopenharmony_ci return CURLE_OK; 215213498266Sopenharmony_ci} 215313498266Sopenharmony_ci 215413498266Sopenharmony_cistatic Curl_recv scp_recv, sftp_recv; 215513498266Sopenharmony_cistatic Curl_send scp_send, sftp_send; 215613498266Sopenharmony_ci 215713498266Sopenharmony_ci/* 215813498266Sopenharmony_ci * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to 215913498266Sopenharmony_ci * do protocol-specific actions at connect-time. 216013498266Sopenharmony_ci */ 216113498266Sopenharmony_cistatic CURLcode myssh_connect(struct Curl_easy *data, bool *done) 216213498266Sopenharmony_ci{ 216313498266Sopenharmony_ci struct ssh_conn *ssh; 216413498266Sopenharmony_ci CURLcode result; 216513498266Sopenharmony_ci struct connectdata *conn = data->conn; 216613498266Sopenharmony_ci curl_socket_t sock = conn->sock[FIRSTSOCKET]; 216713498266Sopenharmony_ci int rc; 216813498266Sopenharmony_ci 216913498266Sopenharmony_ci /* initialize per-handle data if not already */ 217013498266Sopenharmony_ci if(!data->req.p.ssh) 217113498266Sopenharmony_ci myssh_setup_connection(data, conn); 217213498266Sopenharmony_ci 217313498266Sopenharmony_ci /* We default to persistent connections. We set this already in this connect 217413498266Sopenharmony_ci function to make the reuse checks properly be able to check this bit. */ 217513498266Sopenharmony_ci connkeep(conn, "SSH default"); 217613498266Sopenharmony_ci 217713498266Sopenharmony_ci if(conn->handler->protocol & CURLPROTO_SCP) { 217813498266Sopenharmony_ci conn->recv[FIRSTSOCKET] = scp_recv; 217913498266Sopenharmony_ci conn->send[FIRSTSOCKET] = scp_send; 218013498266Sopenharmony_ci } 218113498266Sopenharmony_ci else { 218213498266Sopenharmony_ci conn->recv[FIRSTSOCKET] = sftp_recv; 218313498266Sopenharmony_ci conn->send[FIRSTSOCKET] = sftp_send; 218413498266Sopenharmony_ci } 218513498266Sopenharmony_ci 218613498266Sopenharmony_ci ssh = &conn->proto.sshc; 218713498266Sopenharmony_ci 218813498266Sopenharmony_ci ssh->ssh_session = ssh_new(); 218913498266Sopenharmony_ci if(!ssh->ssh_session) { 219013498266Sopenharmony_ci failf(data, "Failure initialising ssh session"); 219113498266Sopenharmony_ci return CURLE_FAILED_INIT; 219213498266Sopenharmony_ci } 219313498266Sopenharmony_ci 219413498266Sopenharmony_ci rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_HOST, conn->host.name); 219513498266Sopenharmony_ci if(rc != SSH_OK) { 219613498266Sopenharmony_ci failf(data, "Could not set remote host"); 219713498266Sopenharmony_ci return CURLE_FAILED_INIT; 219813498266Sopenharmony_ci } 219913498266Sopenharmony_ci 220013498266Sopenharmony_ci rc = ssh_options_parse_config(ssh->ssh_session, NULL); 220113498266Sopenharmony_ci if(rc != SSH_OK) { 220213498266Sopenharmony_ci infof(data, "Could not parse SSH configuration files"); 220313498266Sopenharmony_ci /* ignore */ 220413498266Sopenharmony_ci } 220513498266Sopenharmony_ci 220613498266Sopenharmony_ci rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_FD, &sock); 220713498266Sopenharmony_ci if(rc != SSH_OK) { 220813498266Sopenharmony_ci failf(data, "Could not set socket"); 220913498266Sopenharmony_ci return CURLE_FAILED_INIT; 221013498266Sopenharmony_ci } 221113498266Sopenharmony_ci 221213498266Sopenharmony_ci if(conn->user && conn->user[0] != '\0') { 221313498266Sopenharmony_ci infof(data, "User: %s", conn->user); 221413498266Sopenharmony_ci rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_USER, conn->user); 221513498266Sopenharmony_ci if(rc != SSH_OK) { 221613498266Sopenharmony_ci failf(data, "Could not set user"); 221713498266Sopenharmony_ci return CURLE_FAILED_INIT; 221813498266Sopenharmony_ci } 221913498266Sopenharmony_ci } 222013498266Sopenharmony_ci 222113498266Sopenharmony_ci if(data->set.str[STRING_SSH_KNOWNHOSTS]) { 222213498266Sopenharmony_ci infof(data, "Known hosts: %s", data->set.str[STRING_SSH_KNOWNHOSTS]); 222313498266Sopenharmony_ci rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_KNOWNHOSTS, 222413498266Sopenharmony_ci data->set.str[STRING_SSH_KNOWNHOSTS]); 222513498266Sopenharmony_ci if(rc != SSH_OK) { 222613498266Sopenharmony_ci failf(data, "Could not set known hosts file path"); 222713498266Sopenharmony_ci return CURLE_FAILED_INIT; 222813498266Sopenharmony_ci } 222913498266Sopenharmony_ci } 223013498266Sopenharmony_ci 223113498266Sopenharmony_ci if(conn->remote_port) { 223213498266Sopenharmony_ci rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_PORT, 223313498266Sopenharmony_ci &conn->remote_port); 223413498266Sopenharmony_ci if(rc != SSH_OK) { 223513498266Sopenharmony_ci failf(data, "Could not set remote port"); 223613498266Sopenharmony_ci return CURLE_FAILED_INIT; 223713498266Sopenharmony_ci } 223813498266Sopenharmony_ci } 223913498266Sopenharmony_ci 224013498266Sopenharmony_ci if(data->set.ssh_compression) { 224113498266Sopenharmony_ci rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_COMPRESSION, 224213498266Sopenharmony_ci "zlib,zlib@openssh.com,none"); 224313498266Sopenharmony_ci if(rc != SSH_OK) { 224413498266Sopenharmony_ci failf(data, "Could not set compression"); 224513498266Sopenharmony_ci return CURLE_FAILED_INIT; 224613498266Sopenharmony_ci } 224713498266Sopenharmony_ci } 224813498266Sopenharmony_ci 224913498266Sopenharmony_ci ssh->privkey = NULL; 225013498266Sopenharmony_ci ssh->pubkey = NULL; 225113498266Sopenharmony_ci 225213498266Sopenharmony_ci if(data->set.str[STRING_SSH_PUBLIC_KEY]) { 225313498266Sopenharmony_ci rc = ssh_pki_import_pubkey_file(data->set.str[STRING_SSH_PUBLIC_KEY], 225413498266Sopenharmony_ci &ssh->pubkey); 225513498266Sopenharmony_ci if(rc != SSH_OK) { 225613498266Sopenharmony_ci failf(data, "Could not load public key file"); 225713498266Sopenharmony_ci return CURLE_FAILED_INIT; 225813498266Sopenharmony_ci } 225913498266Sopenharmony_ci } 226013498266Sopenharmony_ci 226113498266Sopenharmony_ci /* we do not verify here, we do it at the state machine, 226213498266Sopenharmony_ci * after connection */ 226313498266Sopenharmony_ci 226413498266Sopenharmony_ci state(data, SSH_INIT); 226513498266Sopenharmony_ci 226613498266Sopenharmony_ci result = myssh_multi_statemach(data, done); 226713498266Sopenharmony_ci 226813498266Sopenharmony_ci return result; 226913498266Sopenharmony_ci} 227013498266Sopenharmony_ci 227113498266Sopenharmony_ci/* called from multi.c while DOing */ 227213498266Sopenharmony_cistatic CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done) 227313498266Sopenharmony_ci{ 227413498266Sopenharmony_ci CURLcode result; 227513498266Sopenharmony_ci 227613498266Sopenharmony_ci result = myssh_multi_statemach(data, dophase_done); 227713498266Sopenharmony_ci 227813498266Sopenharmony_ci if(*dophase_done) { 227913498266Sopenharmony_ci DEBUGF(infof(data, "DO phase is complete")); 228013498266Sopenharmony_ci } 228113498266Sopenharmony_ci return result; 228213498266Sopenharmony_ci} 228313498266Sopenharmony_ci 228413498266Sopenharmony_ci/* 228513498266Sopenharmony_ci *********************************************************************** 228613498266Sopenharmony_ci * 228713498266Sopenharmony_ci * scp_perform() 228813498266Sopenharmony_ci * 228913498266Sopenharmony_ci * This is the actual DO function for SCP. Get a file according to 229013498266Sopenharmony_ci * the options previously setup. 229113498266Sopenharmony_ci */ 229213498266Sopenharmony_ci 229313498266Sopenharmony_cistatic 229413498266Sopenharmony_ciCURLcode scp_perform(struct Curl_easy *data, 229513498266Sopenharmony_ci bool *connected, bool *dophase_done) 229613498266Sopenharmony_ci{ 229713498266Sopenharmony_ci CURLcode result = CURLE_OK; 229813498266Sopenharmony_ci 229913498266Sopenharmony_ci DEBUGF(infof(data, "DO phase starts")); 230013498266Sopenharmony_ci 230113498266Sopenharmony_ci *dophase_done = FALSE; /* not done yet */ 230213498266Sopenharmony_ci 230313498266Sopenharmony_ci /* start the first command in the DO phase */ 230413498266Sopenharmony_ci state(data, SSH_SCP_TRANS_INIT); 230513498266Sopenharmony_ci 230613498266Sopenharmony_ci result = myssh_multi_statemach(data, dophase_done); 230713498266Sopenharmony_ci 230813498266Sopenharmony_ci *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); 230913498266Sopenharmony_ci 231013498266Sopenharmony_ci if(*dophase_done) { 231113498266Sopenharmony_ci DEBUGF(infof(data, "DO phase is complete")); 231213498266Sopenharmony_ci } 231313498266Sopenharmony_ci 231413498266Sopenharmony_ci return result; 231513498266Sopenharmony_ci} 231613498266Sopenharmony_ci 231713498266Sopenharmony_cistatic CURLcode myssh_do_it(struct Curl_easy *data, bool *done) 231813498266Sopenharmony_ci{ 231913498266Sopenharmony_ci CURLcode result; 232013498266Sopenharmony_ci bool connected = 0; 232113498266Sopenharmony_ci struct connectdata *conn = data->conn; 232213498266Sopenharmony_ci struct ssh_conn *sshc = &conn->proto.sshc; 232313498266Sopenharmony_ci 232413498266Sopenharmony_ci *done = FALSE; /* default to false */ 232513498266Sopenharmony_ci 232613498266Sopenharmony_ci data->req.size = -1; /* make sure this is unknown at this point */ 232713498266Sopenharmony_ci 232813498266Sopenharmony_ci sshc->actualcode = CURLE_OK; /* reset error code */ 232913498266Sopenharmony_ci sshc->secondCreateDirs = 0; /* reset the create dir attempt state 233013498266Sopenharmony_ci variable */ 233113498266Sopenharmony_ci 233213498266Sopenharmony_ci Curl_pgrsSetUploadCounter(data, 0); 233313498266Sopenharmony_ci Curl_pgrsSetDownloadCounter(data, 0); 233413498266Sopenharmony_ci Curl_pgrsSetUploadSize(data, -1); 233513498266Sopenharmony_ci Curl_pgrsSetDownloadSize(data, -1); 233613498266Sopenharmony_ci 233713498266Sopenharmony_ci if(conn->handler->protocol & CURLPROTO_SCP) 233813498266Sopenharmony_ci result = scp_perform(data, &connected, done); 233913498266Sopenharmony_ci else 234013498266Sopenharmony_ci result = sftp_perform(data, &connected, done); 234113498266Sopenharmony_ci 234213498266Sopenharmony_ci return result; 234313498266Sopenharmony_ci} 234413498266Sopenharmony_ci 234513498266Sopenharmony_ci/* BLOCKING, but the function is using the state machine so the only reason 234613498266Sopenharmony_ci this is still blocking is that the multi interface code has no support for 234713498266Sopenharmony_ci disconnecting operations that takes a while */ 234813498266Sopenharmony_cistatic CURLcode scp_disconnect(struct Curl_easy *data, 234913498266Sopenharmony_ci struct connectdata *conn, 235013498266Sopenharmony_ci bool dead_connection) 235113498266Sopenharmony_ci{ 235213498266Sopenharmony_ci CURLcode result = CURLE_OK; 235313498266Sopenharmony_ci struct ssh_conn *ssh = &conn->proto.sshc; 235413498266Sopenharmony_ci (void) dead_connection; 235513498266Sopenharmony_ci 235613498266Sopenharmony_ci if(ssh->ssh_session) { 235713498266Sopenharmony_ci /* only if there's a session still around to use! */ 235813498266Sopenharmony_ci 235913498266Sopenharmony_ci state(data, SSH_SESSION_DISCONNECT); 236013498266Sopenharmony_ci 236113498266Sopenharmony_ci result = myssh_block_statemach(data, TRUE); 236213498266Sopenharmony_ci } 236313498266Sopenharmony_ci 236413498266Sopenharmony_ci return result; 236513498266Sopenharmony_ci} 236613498266Sopenharmony_ci 236713498266Sopenharmony_ci/* generic done function for both SCP and SFTP called from their specific 236813498266Sopenharmony_ci done functions */ 236913498266Sopenharmony_cistatic CURLcode myssh_done(struct Curl_easy *data, CURLcode status) 237013498266Sopenharmony_ci{ 237113498266Sopenharmony_ci CURLcode result = CURLE_OK; 237213498266Sopenharmony_ci struct SSHPROTO *protop = data->req.p.ssh; 237313498266Sopenharmony_ci 237413498266Sopenharmony_ci if(!status) { 237513498266Sopenharmony_ci /* run the state-machine */ 237613498266Sopenharmony_ci result = myssh_block_statemach(data, FALSE); 237713498266Sopenharmony_ci } 237813498266Sopenharmony_ci else 237913498266Sopenharmony_ci result = status; 238013498266Sopenharmony_ci 238113498266Sopenharmony_ci if(protop) 238213498266Sopenharmony_ci Curl_safefree(protop->path); 238313498266Sopenharmony_ci if(Curl_pgrsDone(data)) 238413498266Sopenharmony_ci return CURLE_ABORTED_BY_CALLBACK; 238513498266Sopenharmony_ci 238613498266Sopenharmony_ci data->req.keepon = 0; /* clear all bits */ 238713498266Sopenharmony_ci return result; 238813498266Sopenharmony_ci} 238913498266Sopenharmony_ci 239013498266Sopenharmony_ci 239113498266Sopenharmony_cistatic CURLcode scp_done(struct Curl_easy *data, CURLcode status, 239213498266Sopenharmony_ci bool premature) 239313498266Sopenharmony_ci{ 239413498266Sopenharmony_ci (void) premature; /* not used */ 239513498266Sopenharmony_ci 239613498266Sopenharmony_ci if(!status) 239713498266Sopenharmony_ci state(data, SSH_SCP_DONE); 239813498266Sopenharmony_ci 239913498266Sopenharmony_ci return myssh_done(data, status); 240013498266Sopenharmony_ci 240113498266Sopenharmony_ci} 240213498266Sopenharmony_ci 240313498266Sopenharmony_cistatic ssize_t scp_send(struct Curl_easy *data, int sockindex, 240413498266Sopenharmony_ci const void *mem, size_t len, CURLcode *err) 240513498266Sopenharmony_ci{ 240613498266Sopenharmony_ci int rc; 240713498266Sopenharmony_ci struct connectdata *conn = data->conn; 240813498266Sopenharmony_ci (void) sockindex; /* we only support SCP on the fixed known primary socket */ 240913498266Sopenharmony_ci (void) err; 241013498266Sopenharmony_ci 241113498266Sopenharmony_ci rc = ssh_scp_write(conn->proto.sshc.scp_session, mem, len); 241213498266Sopenharmony_ci 241313498266Sopenharmony_ci#if 0 241413498266Sopenharmony_ci /* The following code is misleading, mostly added as wishful thinking 241513498266Sopenharmony_ci * that libssh at some point will implement non-blocking ssh_scp_write/read. 241613498266Sopenharmony_ci * Currently rc can only be number of bytes read or SSH_ERROR. */ 241713498266Sopenharmony_ci myssh_block2waitfor(conn, (rc == SSH_AGAIN) ? TRUE : FALSE); 241813498266Sopenharmony_ci 241913498266Sopenharmony_ci if(rc == SSH_AGAIN) { 242013498266Sopenharmony_ci *err = CURLE_AGAIN; 242113498266Sopenharmony_ci return 0; 242213498266Sopenharmony_ci } 242313498266Sopenharmony_ci else 242413498266Sopenharmony_ci#endif 242513498266Sopenharmony_ci if(rc != SSH_OK) { 242613498266Sopenharmony_ci *err = CURLE_SSH; 242713498266Sopenharmony_ci return -1; 242813498266Sopenharmony_ci } 242913498266Sopenharmony_ci 243013498266Sopenharmony_ci return len; 243113498266Sopenharmony_ci} 243213498266Sopenharmony_ci 243313498266Sopenharmony_cistatic ssize_t scp_recv(struct Curl_easy *data, int sockindex, 243413498266Sopenharmony_ci char *mem, size_t len, CURLcode *err) 243513498266Sopenharmony_ci{ 243613498266Sopenharmony_ci ssize_t nread; 243713498266Sopenharmony_ci struct connectdata *conn = data->conn; 243813498266Sopenharmony_ci (void) err; 243913498266Sopenharmony_ci (void) sockindex; /* we only support SCP on the fixed known primary socket */ 244013498266Sopenharmony_ci 244113498266Sopenharmony_ci /* libssh returns int */ 244213498266Sopenharmony_ci nread = ssh_scp_read(conn->proto.sshc.scp_session, mem, len); 244313498266Sopenharmony_ci 244413498266Sopenharmony_ci#if 0 244513498266Sopenharmony_ci /* The following code is misleading, mostly added as wishful thinking 244613498266Sopenharmony_ci * that libssh at some point will implement non-blocking ssh_scp_write/read. 244713498266Sopenharmony_ci * Currently rc can only be SSH_OK or SSH_ERROR. */ 244813498266Sopenharmony_ci 244913498266Sopenharmony_ci myssh_block2waitfor(conn, (nread == SSH_AGAIN) ? TRUE : FALSE); 245013498266Sopenharmony_ci if(nread == SSH_AGAIN) { 245113498266Sopenharmony_ci *err = CURLE_AGAIN; 245213498266Sopenharmony_ci nread = -1; 245313498266Sopenharmony_ci } 245413498266Sopenharmony_ci#endif 245513498266Sopenharmony_ci 245613498266Sopenharmony_ci return nread; 245713498266Sopenharmony_ci} 245813498266Sopenharmony_ci 245913498266Sopenharmony_ci/* 246013498266Sopenharmony_ci * =============== SFTP =============== 246113498266Sopenharmony_ci */ 246213498266Sopenharmony_ci 246313498266Sopenharmony_ci/* 246413498266Sopenharmony_ci *********************************************************************** 246513498266Sopenharmony_ci * 246613498266Sopenharmony_ci * sftp_perform() 246713498266Sopenharmony_ci * 246813498266Sopenharmony_ci * This is the actual DO function for SFTP. Get a file/directory according to 246913498266Sopenharmony_ci * the options previously setup. 247013498266Sopenharmony_ci */ 247113498266Sopenharmony_ci 247213498266Sopenharmony_cistatic 247313498266Sopenharmony_ciCURLcode sftp_perform(struct Curl_easy *data, 247413498266Sopenharmony_ci bool *connected, 247513498266Sopenharmony_ci bool *dophase_done) 247613498266Sopenharmony_ci{ 247713498266Sopenharmony_ci CURLcode result = CURLE_OK; 247813498266Sopenharmony_ci 247913498266Sopenharmony_ci DEBUGF(infof(data, "DO phase starts")); 248013498266Sopenharmony_ci 248113498266Sopenharmony_ci *dophase_done = FALSE; /* not done yet */ 248213498266Sopenharmony_ci 248313498266Sopenharmony_ci /* start the first command in the DO phase */ 248413498266Sopenharmony_ci state(data, SSH_SFTP_QUOTE_INIT); 248513498266Sopenharmony_ci 248613498266Sopenharmony_ci /* run the state-machine */ 248713498266Sopenharmony_ci result = myssh_multi_statemach(data, dophase_done); 248813498266Sopenharmony_ci 248913498266Sopenharmony_ci *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET); 249013498266Sopenharmony_ci 249113498266Sopenharmony_ci if(*dophase_done) { 249213498266Sopenharmony_ci DEBUGF(infof(data, "DO phase is complete")); 249313498266Sopenharmony_ci } 249413498266Sopenharmony_ci 249513498266Sopenharmony_ci return result; 249613498266Sopenharmony_ci} 249713498266Sopenharmony_ci 249813498266Sopenharmony_ci/* called from multi.c while DOing */ 249913498266Sopenharmony_cistatic CURLcode sftp_doing(struct Curl_easy *data, 250013498266Sopenharmony_ci bool *dophase_done) 250113498266Sopenharmony_ci{ 250213498266Sopenharmony_ci CURLcode result = myssh_multi_statemach(data, dophase_done); 250313498266Sopenharmony_ci if(*dophase_done) { 250413498266Sopenharmony_ci DEBUGF(infof(data, "DO phase is complete")); 250513498266Sopenharmony_ci } 250613498266Sopenharmony_ci return result; 250713498266Sopenharmony_ci} 250813498266Sopenharmony_ci 250913498266Sopenharmony_ci/* BLOCKING, but the function is using the state machine so the only reason 251013498266Sopenharmony_ci this is still blocking is that the multi interface code has no support for 251113498266Sopenharmony_ci disconnecting operations that takes a while */ 251213498266Sopenharmony_cistatic CURLcode sftp_disconnect(struct Curl_easy *data, 251313498266Sopenharmony_ci struct connectdata *conn, 251413498266Sopenharmony_ci bool dead_connection) 251513498266Sopenharmony_ci{ 251613498266Sopenharmony_ci CURLcode result = CURLE_OK; 251713498266Sopenharmony_ci (void) dead_connection; 251813498266Sopenharmony_ci 251913498266Sopenharmony_ci DEBUGF(infof(data, "SSH DISCONNECT starts now")); 252013498266Sopenharmony_ci 252113498266Sopenharmony_ci if(conn->proto.sshc.ssh_session) { 252213498266Sopenharmony_ci /* only if there's a session still around to use! */ 252313498266Sopenharmony_ci state(data, SSH_SFTP_SHUTDOWN); 252413498266Sopenharmony_ci result = myssh_block_statemach(data, TRUE); 252513498266Sopenharmony_ci } 252613498266Sopenharmony_ci 252713498266Sopenharmony_ci DEBUGF(infof(data, "SSH DISCONNECT is done")); 252813498266Sopenharmony_ci 252913498266Sopenharmony_ci return result; 253013498266Sopenharmony_ci 253113498266Sopenharmony_ci} 253213498266Sopenharmony_ci 253313498266Sopenharmony_cistatic CURLcode sftp_done(struct Curl_easy *data, CURLcode status, 253413498266Sopenharmony_ci bool premature) 253513498266Sopenharmony_ci{ 253613498266Sopenharmony_ci struct connectdata *conn = data->conn; 253713498266Sopenharmony_ci struct ssh_conn *sshc = &conn->proto.sshc; 253813498266Sopenharmony_ci 253913498266Sopenharmony_ci if(!status) { 254013498266Sopenharmony_ci /* Post quote commands are executed after the SFTP_CLOSE state to avoid 254113498266Sopenharmony_ci errors that could happen due to open file handles during POSTQUOTE 254213498266Sopenharmony_ci operation */ 254313498266Sopenharmony_ci if(!premature && data->set.postquote && !conn->bits.retry) 254413498266Sopenharmony_ci sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT; 254513498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 254613498266Sopenharmony_ci } 254713498266Sopenharmony_ci return myssh_done(data, status); 254813498266Sopenharmony_ci} 254913498266Sopenharmony_ci 255013498266Sopenharmony_ci/* return number of sent bytes */ 255113498266Sopenharmony_cistatic ssize_t sftp_send(struct Curl_easy *data, int sockindex, 255213498266Sopenharmony_ci const void *mem, size_t len, CURLcode *err) 255313498266Sopenharmony_ci{ 255413498266Sopenharmony_ci ssize_t nwrite; 255513498266Sopenharmony_ci struct connectdata *conn = data->conn; 255613498266Sopenharmony_ci (void)sockindex; 255713498266Sopenharmony_ci 255813498266Sopenharmony_ci /* limit the writes to the maximum specified in Section 3 of 255913498266Sopenharmony_ci * https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02 256013498266Sopenharmony_ci */ 256113498266Sopenharmony_ci if(len > 32768) 256213498266Sopenharmony_ci len = 32768; 256313498266Sopenharmony_ci 256413498266Sopenharmony_ci nwrite = sftp_write(conn->proto.sshc.sftp_file, mem, len); 256513498266Sopenharmony_ci 256613498266Sopenharmony_ci myssh_block2waitfor(conn, FALSE); 256713498266Sopenharmony_ci 256813498266Sopenharmony_ci#if 0 /* not returned by libssh on write */ 256913498266Sopenharmony_ci if(nwrite == SSH_AGAIN) { 257013498266Sopenharmony_ci *err = CURLE_AGAIN; 257113498266Sopenharmony_ci nwrite = 0; 257213498266Sopenharmony_ci } 257313498266Sopenharmony_ci else 257413498266Sopenharmony_ci#endif 257513498266Sopenharmony_ci if(nwrite < 0) { 257613498266Sopenharmony_ci *err = CURLE_SSH; 257713498266Sopenharmony_ci nwrite = -1; 257813498266Sopenharmony_ci } 257913498266Sopenharmony_ci 258013498266Sopenharmony_ci return nwrite; 258113498266Sopenharmony_ci} 258213498266Sopenharmony_ci 258313498266Sopenharmony_ci/* 258413498266Sopenharmony_ci * Return number of received (decrypted) bytes 258513498266Sopenharmony_ci * or <0 on error 258613498266Sopenharmony_ci */ 258713498266Sopenharmony_cistatic ssize_t sftp_recv(struct Curl_easy *data, int sockindex, 258813498266Sopenharmony_ci char *mem, size_t len, CURLcode *err) 258913498266Sopenharmony_ci{ 259013498266Sopenharmony_ci ssize_t nread; 259113498266Sopenharmony_ci struct connectdata *conn = data->conn; 259213498266Sopenharmony_ci (void)sockindex; 259313498266Sopenharmony_ci 259413498266Sopenharmony_ci DEBUGASSERT(len < CURL_MAX_READ_SIZE); 259513498266Sopenharmony_ci 259613498266Sopenharmony_ci switch(conn->proto.sshc.sftp_recv_state) { 259713498266Sopenharmony_ci case 0: 259813498266Sopenharmony_ci conn->proto.sshc.sftp_file_index = 259913498266Sopenharmony_ci sftp_async_read_begin(conn->proto.sshc.sftp_file, 260013498266Sopenharmony_ci (uint32_t)len); 260113498266Sopenharmony_ci if(conn->proto.sshc.sftp_file_index < 0) { 260213498266Sopenharmony_ci *err = CURLE_RECV_ERROR; 260313498266Sopenharmony_ci return -1; 260413498266Sopenharmony_ci } 260513498266Sopenharmony_ci 260613498266Sopenharmony_ci FALLTHROUGH(); 260713498266Sopenharmony_ci case 1: 260813498266Sopenharmony_ci conn->proto.sshc.sftp_recv_state = 1; 260913498266Sopenharmony_ci 261013498266Sopenharmony_ci nread = sftp_async_read(conn->proto.sshc.sftp_file, 261113498266Sopenharmony_ci mem, (uint32_t)len, 261213498266Sopenharmony_ci conn->proto.sshc.sftp_file_index); 261313498266Sopenharmony_ci 261413498266Sopenharmony_ci myssh_block2waitfor(conn, (nread == SSH_AGAIN)?TRUE:FALSE); 261513498266Sopenharmony_ci 261613498266Sopenharmony_ci if(nread == SSH_AGAIN) { 261713498266Sopenharmony_ci *err = CURLE_AGAIN; 261813498266Sopenharmony_ci return -1; 261913498266Sopenharmony_ci } 262013498266Sopenharmony_ci else if(nread < 0) { 262113498266Sopenharmony_ci *err = CURLE_RECV_ERROR; 262213498266Sopenharmony_ci return -1; 262313498266Sopenharmony_ci } 262413498266Sopenharmony_ci 262513498266Sopenharmony_ci conn->proto.sshc.sftp_recv_state = 0; 262613498266Sopenharmony_ci return nread; 262713498266Sopenharmony_ci 262813498266Sopenharmony_ci default: 262913498266Sopenharmony_ci /* we never reach here */ 263013498266Sopenharmony_ci return -1; 263113498266Sopenharmony_ci } 263213498266Sopenharmony_ci} 263313498266Sopenharmony_ci 263413498266Sopenharmony_cistatic void sftp_quote(struct Curl_easy *data) 263513498266Sopenharmony_ci{ 263613498266Sopenharmony_ci const char *cp; 263713498266Sopenharmony_ci struct connectdata *conn = data->conn; 263813498266Sopenharmony_ci struct SSHPROTO *protop = data->req.p.ssh; 263913498266Sopenharmony_ci struct ssh_conn *sshc = &conn->proto.sshc; 264013498266Sopenharmony_ci CURLcode result; 264113498266Sopenharmony_ci 264213498266Sopenharmony_ci /* 264313498266Sopenharmony_ci * Support some of the "FTP" commands 264413498266Sopenharmony_ci */ 264513498266Sopenharmony_ci char *cmd = sshc->quote_item->data; 264613498266Sopenharmony_ci sshc->acceptfail = FALSE; 264713498266Sopenharmony_ci 264813498266Sopenharmony_ci /* if a command starts with an asterisk, which a legal SFTP command never 264913498266Sopenharmony_ci can, the command will be allowed to fail without it causing any 265013498266Sopenharmony_ci aborts or cancels etc. It will cause libcurl to act as if the command 265113498266Sopenharmony_ci is successful, whatever the server responds. */ 265213498266Sopenharmony_ci 265313498266Sopenharmony_ci if(cmd[0] == '*') { 265413498266Sopenharmony_ci cmd++; 265513498266Sopenharmony_ci sshc->acceptfail = TRUE; 265613498266Sopenharmony_ci } 265713498266Sopenharmony_ci 265813498266Sopenharmony_ci if(strcasecompare("pwd", cmd)) { 265913498266Sopenharmony_ci /* output debug output if that is requested */ 266013498266Sopenharmony_ci char *tmp = aprintf("257 \"%s\" is current directory.\n", 266113498266Sopenharmony_ci protop->path); 266213498266Sopenharmony_ci if(!tmp) { 266313498266Sopenharmony_ci sshc->actualcode = CURLE_OUT_OF_MEMORY; 266413498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 266513498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 266613498266Sopenharmony_ci return; 266713498266Sopenharmony_ci } 266813498266Sopenharmony_ci Curl_debug(data, CURLINFO_HEADER_OUT, (char *) "PWD\n", 4); 266913498266Sopenharmony_ci Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp)); 267013498266Sopenharmony_ci 267113498266Sopenharmony_ci /* this sends an FTP-like "header" to the header callback so that the 267213498266Sopenharmony_ci current directory can be read very similar to how it is read when 267313498266Sopenharmony_ci using ordinary FTP. */ 267413498266Sopenharmony_ci result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp)); 267513498266Sopenharmony_ci free(tmp); 267613498266Sopenharmony_ci if(result) { 267713498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 267813498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 267913498266Sopenharmony_ci sshc->actualcode = result; 268013498266Sopenharmony_ci } 268113498266Sopenharmony_ci else 268213498266Sopenharmony_ci state(data, SSH_SFTP_NEXT_QUOTE); 268313498266Sopenharmony_ci return; 268413498266Sopenharmony_ci } 268513498266Sopenharmony_ci 268613498266Sopenharmony_ci /* 268713498266Sopenharmony_ci * the arguments following the command must be separated from the 268813498266Sopenharmony_ci * command with a space so we can check for it unconditionally 268913498266Sopenharmony_ci */ 269013498266Sopenharmony_ci cp = strchr(cmd, ' '); 269113498266Sopenharmony_ci if(!cp) { 269213498266Sopenharmony_ci failf(data, "Syntax error in SFTP command. Supply parameter(s)"); 269313498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 269413498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 269513498266Sopenharmony_ci sshc->actualcode = CURLE_QUOTE_ERROR; 269613498266Sopenharmony_ci return; 269713498266Sopenharmony_ci } 269813498266Sopenharmony_ci 269913498266Sopenharmony_ci /* 270013498266Sopenharmony_ci * also, every command takes at least one argument so we get that 270113498266Sopenharmony_ci * first argument right now 270213498266Sopenharmony_ci */ 270313498266Sopenharmony_ci result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir); 270413498266Sopenharmony_ci if(result) { 270513498266Sopenharmony_ci if(result == CURLE_OUT_OF_MEMORY) 270613498266Sopenharmony_ci failf(data, "Out of memory"); 270713498266Sopenharmony_ci else 270813498266Sopenharmony_ci failf(data, "Syntax error: Bad first parameter"); 270913498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 271013498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 271113498266Sopenharmony_ci sshc->actualcode = result; 271213498266Sopenharmony_ci return; 271313498266Sopenharmony_ci } 271413498266Sopenharmony_ci 271513498266Sopenharmony_ci /* 271613498266Sopenharmony_ci * SFTP is a binary protocol, so we don't send text commands 271713498266Sopenharmony_ci * to the server. Instead, we scan for commands used by 271813498266Sopenharmony_ci * OpenSSH's sftp program and call the appropriate libssh 271913498266Sopenharmony_ci * functions. 272013498266Sopenharmony_ci */ 272113498266Sopenharmony_ci if(strncasecompare(cmd, "chgrp ", 6) || 272213498266Sopenharmony_ci strncasecompare(cmd, "chmod ", 6) || 272313498266Sopenharmony_ci strncasecompare(cmd, "chown ", 6) || 272413498266Sopenharmony_ci strncasecompare(cmd, "atime ", 6) || 272513498266Sopenharmony_ci strncasecompare(cmd, "mtime ", 6)) { 272613498266Sopenharmony_ci /* attribute change */ 272713498266Sopenharmony_ci 272813498266Sopenharmony_ci /* sshc->quote_path1 contains the mode to set */ 272913498266Sopenharmony_ci /* get the destination */ 273013498266Sopenharmony_ci result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); 273113498266Sopenharmony_ci if(result) { 273213498266Sopenharmony_ci if(result == CURLE_OUT_OF_MEMORY) 273313498266Sopenharmony_ci failf(data, "Out of memory"); 273413498266Sopenharmony_ci else 273513498266Sopenharmony_ci failf(data, "Syntax error in chgrp/chmod/chown/atime/mtime: " 273613498266Sopenharmony_ci "Bad second parameter"); 273713498266Sopenharmony_ci Curl_safefree(sshc->quote_path1); 273813498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 273913498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 274013498266Sopenharmony_ci sshc->actualcode = result; 274113498266Sopenharmony_ci return; 274213498266Sopenharmony_ci } 274313498266Sopenharmony_ci sshc->quote_attrs = NULL; 274413498266Sopenharmony_ci state(data, SSH_SFTP_QUOTE_STAT); 274513498266Sopenharmony_ci return; 274613498266Sopenharmony_ci } 274713498266Sopenharmony_ci if(strncasecompare(cmd, "ln ", 3) || 274813498266Sopenharmony_ci strncasecompare(cmd, "symlink ", 8)) { 274913498266Sopenharmony_ci /* symbolic linking */ 275013498266Sopenharmony_ci /* sshc->quote_path1 is the source */ 275113498266Sopenharmony_ci /* get the destination */ 275213498266Sopenharmony_ci result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); 275313498266Sopenharmony_ci if(result) { 275413498266Sopenharmony_ci if(result == CURLE_OUT_OF_MEMORY) 275513498266Sopenharmony_ci failf(data, "Out of memory"); 275613498266Sopenharmony_ci else 275713498266Sopenharmony_ci failf(data, "Syntax error in ln/symlink: Bad second parameter"); 275813498266Sopenharmony_ci Curl_safefree(sshc->quote_path1); 275913498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 276013498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 276113498266Sopenharmony_ci sshc->actualcode = result; 276213498266Sopenharmony_ci return; 276313498266Sopenharmony_ci } 276413498266Sopenharmony_ci state(data, SSH_SFTP_QUOTE_SYMLINK); 276513498266Sopenharmony_ci return; 276613498266Sopenharmony_ci } 276713498266Sopenharmony_ci else if(strncasecompare(cmd, "mkdir ", 6)) { 276813498266Sopenharmony_ci /* create dir */ 276913498266Sopenharmony_ci state(data, SSH_SFTP_QUOTE_MKDIR); 277013498266Sopenharmony_ci return; 277113498266Sopenharmony_ci } 277213498266Sopenharmony_ci else if(strncasecompare(cmd, "rename ", 7)) { 277313498266Sopenharmony_ci /* rename file */ 277413498266Sopenharmony_ci /* first param is the source path */ 277513498266Sopenharmony_ci /* second param is the dest. path */ 277613498266Sopenharmony_ci result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir); 277713498266Sopenharmony_ci if(result) { 277813498266Sopenharmony_ci if(result == CURLE_OUT_OF_MEMORY) 277913498266Sopenharmony_ci failf(data, "Out of memory"); 278013498266Sopenharmony_ci else 278113498266Sopenharmony_ci failf(data, "Syntax error in rename: Bad second parameter"); 278213498266Sopenharmony_ci Curl_safefree(sshc->quote_path1); 278313498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 278413498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 278513498266Sopenharmony_ci sshc->actualcode = result; 278613498266Sopenharmony_ci return; 278713498266Sopenharmony_ci } 278813498266Sopenharmony_ci state(data, SSH_SFTP_QUOTE_RENAME); 278913498266Sopenharmony_ci return; 279013498266Sopenharmony_ci } 279113498266Sopenharmony_ci else if(strncasecompare(cmd, "rmdir ", 6)) { 279213498266Sopenharmony_ci /* delete dir */ 279313498266Sopenharmony_ci state(data, SSH_SFTP_QUOTE_RMDIR); 279413498266Sopenharmony_ci return; 279513498266Sopenharmony_ci } 279613498266Sopenharmony_ci else if(strncasecompare(cmd, "rm ", 3)) { 279713498266Sopenharmony_ci state(data, SSH_SFTP_QUOTE_UNLINK); 279813498266Sopenharmony_ci return; 279913498266Sopenharmony_ci } 280013498266Sopenharmony_ci#ifdef HAS_STATVFS_SUPPORT 280113498266Sopenharmony_ci else if(strncasecompare(cmd, "statvfs ", 8)) { 280213498266Sopenharmony_ci state(data, SSH_SFTP_QUOTE_STATVFS); 280313498266Sopenharmony_ci return; 280413498266Sopenharmony_ci } 280513498266Sopenharmony_ci#endif 280613498266Sopenharmony_ci 280713498266Sopenharmony_ci failf(data, "Unknown SFTP command"); 280813498266Sopenharmony_ci Curl_safefree(sshc->quote_path1); 280913498266Sopenharmony_ci Curl_safefree(sshc->quote_path2); 281013498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 281113498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 281213498266Sopenharmony_ci sshc->actualcode = CURLE_QUOTE_ERROR; 281313498266Sopenharmony_ci} 281413498266Sopenharmony_ci 281513498266Sopenharmony_cistatic void sftp_quote_stat(struct Curl_easy *data) 281613498266Sopenharmony_ci{ 281713498266Sopenharmony_ci struct connectdata *conn = data->conn; 281813498266Sopenharmony_ci struct ssh_conn *sshc = &conn->proto.sshc; 281913498266Sopenharmony_ci char *cmd = sshc->quote_item->data; 282013498266Sopenharmony_ci sshc->acceptfail = FALSE; 282113498266Sopenharmony_ci 282213498266Sopenharmony_ci /* if a command starts with an asterisk, which a legal SFTP command never 282313498266Sopenharmony_ci can, the command will be allowed to fail without it causing any 282413498266Sopenharmony_ci aborts or cancels etc. It will cause libcurl to act as if the command 282513498266Sopenharmony_ci is successful, whatever the server responds. */ 282613498266Sopenharmony_ci 282713498266Sopenharmony_ci if(cmd[0] == '*') { 282813498266Sopenharmony_ci cmd++; 282913498266Sopenharmony_ci sshc->acceptfail = TRUE; 283013498266Sopenharmony_ci } 283113498266Sopenharmony_ci 283213498266Sopenharmony_ci /* We read the file attributes, store them in sshc->quote_attrs 283313498266Sopenharmony_ci * and modify them accordingly to command. Then we switch to 283413498266Sopenharmony_ci * QUOTE_SETSTAT state to write new ones. 283513498266Sopenharmony_ci */ 283613498266Sopenharmony_ci 283713498266Sopenharmony_ci if(sshc->quote_attrs) 283813498266Sopenharmony_ci sftp_attributes_free(sshc->quote_attrs); 283913498266Sopenharmony_ci sshc->quote_attrs = sftp_stat(sshc->sftp_session, sshc->quote_path2); 284013498266Sopenharmony_ci if(!sshc->quote_attrs) { 284113498266Sopenharmony_ci Curl_safefree(sshc->quote_path1); 284213498266Sopenharmony_ci Curl_safefree(sshc->quote_path2); 284313498266Sopenharmony_ci failf(data, "Attempt to get SFTP stats failed: %d", 284413498266Sopenharmony_ci sftp_get_error(sshc->sftp_session)); 284513498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 284613498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 284713498266Sopenharmony_ci sshc->actualcode = CURLE_QUOTE_ERROR; 284813498266Sopenharmony_ci return; 284913498266Sopenharmony_ci } 285013498266Sopenharmony_ci 285113498266Sopenharmony_ci /* Now set the new attributes... */ 285213498266Sopenharmony_ci if(strncasecompare(cmd, "chgrp", 5)) { 285313498266Sopenharmony_ci sshc->quote_attrs->gid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10); 285413498266Sopenharmony_ci if(sshc->quote_attrs->gid == 0 && !ISDIGIT(sshc->quote_path1[0]) && 285513498266Sopenharmony_ci !sshc->acceptfail) { 285613498266Sopenharmony_ci Curl_safefree(sshc->quote_path1); 285713498266Sopenharmony_ci Curl_safefree(sshc->quote_path2); 285813498266Sopenharmony_ci failf(data, "Syntax error: chgrp gid not a number"); 285913498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 286013498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 286113498266Sopenharmony_ci sshc->actualcode = CURLE_QUOTE_ERROR; 286213498266Sopenharmony_ci return; 286313498266Sopenharmony_ci } 286413498266Sopenharmony_ci sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID; 286513498266Sopenharmony_ci } 286613498266Sopenharmony_ci else if(strncasecompare(cmd, "chmod", 5)) { 286713498266Sopenharmony_ci mode_t perms; 286813498266Sopenharmony_ci perms = (mode_t)strtoul(sshc->quote_path1, NULL, 8); 286913498266Sopenharmony_ci /* permissions are octal */ 287013498266Sopenharmony_ci if(perms == 0 && !ISDIGIT(sshc->quote_path1[0])) { 287113498266Sopenharmony_ci Curl_safefree(sshc->quote_path1); 287213498266Sopenharmony_ci Curl_safefree(sshc->quote_path2); 287313498266Sopenharmony_ci failf(data, "Syntax error: chmod permissions not a number"); 287413498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 287513498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 287613498266Sopenharmony_ci sshc->actualcode = CURLE_QUOTE_ERROR; 287713498266Sopenharmony_ci return; 287813498266Sopenharmony_ci } 287913498266Sopenharmony_ci sshc->quote_attrs->permissions = perms; 288013498266Sopenharmony_ci sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_PERMISSIONS; 288113498266Sopenharmony_ci } 288213498266Sopenharmony_ci else if(strncasecompare(cmd, "chown", 5)) { 288313498266Sopenharmony_ci sshc->quote_attrs->uid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10); 288413498266Sopenharmony_ci if(sshc->quote_attrs->uid == 0 && !ISDIGIT(sshc->quote_path1[0]) && 288513498266Sopenharmony_ci !sshc->acceptfail) { 288613498266Sopenharmony_ci Curl_safefree(sshc->quote_path1); 288713498266Sopenharmony_ci Curl_safefree(sshc->quote_path2); 288813498266Sopenharmony_ci failf(data, "Syntax error: chown uid not a number"); 288913498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 289013498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 289113498266Sopenharmony_ci sshc->actualcode = CURLE_QUOTE_ERROR; 289213498266Sopenharmony_ci return; 289313498266Sopenharmony_ci } 289413498266Sopenharmony_ci sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID; 289513498266Sopenharmony_ci } 289613498266Sopenharmony_ci else if(strncasecompare(cmd, "atime", 5) || 289713498266Sopenharmony_ci strncasecompare(cmd, "mtime", 5)) { 289813498266Sopenharmony_ci time_t date = Curl_getdate_capped(sshc->quote_path1); 289913498266Sopenharmony_ci bool fail = FALSE; 290013498266Sopenharmony_ci if(date == -1) { 290113498266Sopenharmony_ci failf(data, "incorrect date format for %.*s", 5, cmd); 290213498266Sopenharmony_ci fail = TRUE; 290313498266Sopenharmony_ci } 290413498266Sopenharmony_ci#if SIZEOF_TIME_T > 4 290513498266Sopenharmony_ci else if(date > 0xffffffff) { 290613498266Sopenharmony_ci failf(data, "date overflow"); 290713498266Sopenharmony_ci fail = TRUE; /* avoid setting a capped time */ 290813498266Sopenharmony_ci } 290913498266Sopenharmony_ci#endif 291013498266Sopenharmony_ci if(fail) { 291113498266Sopenharmony_ci Curl_safefree(sshc->quote_path1); 291213498266Sopenharmony_ci Curl_safefree(sshc->quote_path2); 291313498266Sopenharmony_ci state(data, SSH_SFTP_CLOSE); 291413498266Sopenharmony_ci sshc->nextstate = SSH_NO_STATE; 291513498266Sopenharmony_ci sshc->actualcode = CURLE_QUOTE_ERROR; 291613498266Sopenharmony_ci return; 291713498266Sopenharmony_ci } 291813498266Sopenharmony_ci if(strncasecompare(cmd, "atime", 5)) 291913498266Sopenharmony_ci sshc->quote_attrs->atime = (uint32_t)date; 292013498266Sopenharmony_ci else /* mtime */ 292113498266Sopenharmony_ci sshc->quote_attrs->mtime = (uint32_t)date; 292213498266Sopenharmony_ci 292313498266Sopenharmony_ci sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_ACMODTIME; 292413498266Sopenharmony_ci } 292513498266Sopenharmony_ci 292613498266Sopenharmony_ci /* Now send the completed structure... */ 292713498266Sopenharmony_ci state(data, SSH_SFTP_QUOTE_SETSTAT); 292813498266Sopenharmony_ci return; 292913498266Sopenharmony_ci} 293013498266Sopenharmony_ci 293113498266Sopenharmony_ciCURLcode Curl_ssh_init(void) 293213498266Sopenharmony_ci{ 293313498266Sopenharmony_ci if(ssh_init()) { 293413498266Sopenharmony_ci DEBUGF(fprintf(stderr, "Error: libssh_init failed\n")); 293513498266Sopenharmony_ci return CURLE_FAILED_INIT; 293613498266Sopenharmony_ci } 293713498266Sopenharmony_ci return CURLE_OK; 293813498266Sopenharmony_ci} 293913498266Sopenharmony_ci 294013498266Sopenharmony_civoid Curl_ssh_cleanup(void) 294113498266Sopenharmony_ci{ 294213498266Sopenharmony_ci (void)ssh_finalize(); 294313498266Sopenharmony_ci} 294413498266Sopenharmony_ci 294513498266Sopenharmony_civoid Curl_ssh_version(char *buffer, size_t buflen) 294613498266Sopenharmony_ci{ 294713498266Sopenharmony_ci (void)msnprintf(buffer, buflen, "libssh/%s", ssh_version(0)); 294813498266Sopenharmony_ci} 294913498266Sopenharmony_ci 295013498266Sopenharmony_ci#endif /* USE_LIBSSH */ 2951