blob: 3e317e87c998a3a3193d4602708a7f3c21d52871 [file] [log] [blame]
Elliott Hughes0128fe42018-02-27 14:57:55 -08001/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
Elliott Hughes34dd5f42021-08-10 13:01:18 -07008 * Copyright (C) 2017 - 2021 Red Hat, Inc.
Elliott Hughes0128fe42018-02-27 14:57:55 -08009 *
10 * Authors: Nikos Mavrogiannopoulos, Tomas Mraz, Stanislav Zidek,
11 * Robert Kolcun, Andreas Schneider
12 *
13 * This software is licensed as described in the file COPYING, which
14 * you should have received as part of this distribution. The terms
Elliott Hughes34dd5f42021-08-10 13:01:18 -070015 * are also available at https://curl.se/docs/copyright.html.
Elliott Hughes0128fe42018-02-27 14:57:55 -080016 *
17 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
18 * copies of the Software, and permit persons to whom the Software is
19 * furnished to do so, under the terms of the COPYING file.
20 *
21 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
22 * KIND, either express or implied.
23 *
24 ***************************************************************************/
25
26#include "curl_setup.h"
27
28#ifdef USE_LIBSSH
29
30#include <limits.h>
31
32#include <libssh/libssh.h>
33#include <libssh/sftp.h>
34
35#ifdef HAVE_FCNTL_H
36#include <fcntl.h>
37#endif
38
39#ifdef HAVE_NETINET_IN_H
40#include <netinet/in.h>
41#endif
42#ifdef HAVE_ARPA_INET_H
43#include <arpa/inet.h>
44#endif
45#ifdef HAVE_UTSNAME_H
46#include <sys/utsname.h>
47#endif
48#ifdef HAVE_NETDB_H
49#include <netdb.h>
50#endif
51#ifdef __VMS
52#include <in.h>
53#include <inet.h>
54#endif
55
56#if (defined(NETWARE) && defined(__NOVELL_LIBC__))
57#undef in_addr_t
58#define in_addr_t unsigned long
59#endif
60
61#include <curl/curl.h>
62#include "urldata.h"
63#include "sendf.h"
64#include "hostip.h"
65#include "progress.h"
66#include "transfer.h"
67#include "escape.h"
68#include "http.h" /* for HTTP proxy tunnel stuff */
69#include "ssh.h"
70#include "url.h"
71#include "speedcheck.h"
72#include "getinfo.h"
73#include "strdup.h"
74#include "strcase.h"
75#include "vtls/vtls.h"
76#include "connect.h"
Elliott Hughes0128fe42018-02-27 14:57:55 -080077#include "inet_ntop.h"
78#include "parsedate.h" /* for the week day and month names */
79#include "sockaddr.h" /* required for Curl_sockaddr_storage */
80#include "strtoofft.h"
81#include "multiif.h"
82#include "select.h"
83#include "warnless.h"
84
85/* for permission and open flags */
86#include <sys/types.h>
87#include <sys/stat.h>
88#include <unistd.h>
89#include <fcntl.h>
90
91/* The last 3 #include files should be in this order */
92#include "curl_printf.h"
93#include "curl_memory.h"
94#include "memdebug.h"
95#include "curl_path.h"
96
Haibo Huang51d9d882019-02-06 01:36:06 -080097/* A recent macro provided by libssh. Or make our own. */
98#ifndef SSH_STRING_FREE_CHAR
Haibo Huangb51266f2020-03-04 02:22:48 -080099#define SSH_STRING_FREE_CHAR(x) \
100 do { \
101 if(x) { \
102 ssh_string_free_char(x); \
103 x = NULL; \
104 } \
105 } while(0)
Haibo Huang51d9d882019-02-06 01:36:06 -0800106#endif
107
Elliott Hughes0128fe42018-02-27 14:57:55 -0800108/* Local functions: */
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700109static CURLcode myssh_connect(struct Curl_easy *data, bool *done);
110static CURLcode myssh_multi_statemach(struct Curl_easy *data,
Elliott Hughes0128fe42018-02-27 14:57:55 -0800111 bool *done);
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700112static CURLcode myssh_do_it(struct Curl_easy *data, bool *done);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800113
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700114static CURLcode scp_done(struct Curl_easy *data,
Elliott Hughes0128fe42018-02-27 14:57:55 -0800115 CURLcode, bool premature);
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700116static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done);
117static CURLcode scp_disconnect(struct Curl_easy *data,
118 struct connectdata *conn,
Elliott Hughes0128fe42018-02-27 14:57:55 -0800119 bool dead_connection);
120
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700121static CURLcode sftp_done(struct Curl_easy *data,
Elliott Hughes0128fe42018-02-27 14:57:55 -0800122 CURLcode, bool premature);
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700123static CURLcode sftp_doing(struct Curl_easy *data,
Elliott Hughes0128fe42018-02-27 14:57:55 -0800124 bool *dophase_done);
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700125static CURLcode sftp_disconnect(struct Curl_easy *data,
126 struct connectdata *conn,
127 bool dead);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800128static
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700129CURLcode sftp_perform(struct Curl_easy *data,
Elliott Hughes0128fe42018-02-27 14:57:55 -0800130 bool *connected,
131 bool *dophase_done);
132
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700133static void sftp_quote(struct Curl_easy *data);
134static void sftp_quote_stat(struct Curl_easy *data);
135static int myssh_getsock(struct Curl_easy *data,
136 struct connectdata *conn, curl_socket_t *sock);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800137
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700138static CURLcode myssh_setup_connection(struct Curl_easy *data,
139 struct connectdata *conn);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800140
141/*
142 * SCP protocol handler.
143 */
144
145const struct Curl_handler Curl_handler_scp = {
146 "SCP", /* scheme */
147 myssh_setup_connection, /* setup_connection */
148 myssh_do_it, /* do_it */
149 scp_done, /* done */
150 ZERO_NULL, /* do_more */
151 myssh_connect, /* connect_it */
152 myssh_multi_statemach, /* connecting */
153 scp_doing, /* doing */
154 myssh_getsock, /* proto_getsock */
155 myssh_getsock, /* doing_getsock */
156 ZERO_NULL, /* domore_getsock */
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700157 myssh_getsock, /* perform_getsock */
Elliott Hughes0128fe42018-02-27 14:57:55 -0800158 scp_disconnect, /* disconnect */
159 ZERO_NULL, /* readwrite */
160 ZERO_NULL, /* connection_check */
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700161 ZERO_NULL, /* attach connection */
Elliott Hughes0128fe42018-02-27 14:57:55 -0800162 PORT_SSH, /* defport */
163 CURLPROTO_SCP, /* protocol */
Haibo Huangb5a52b92020-10-28 22:18:23 -0700164 CURLPROTO_SCP, /* family */
Elliott Hughes0128fe42018-02-27 14:57:55 -0800165 PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
166};
167
168/*
169 * SFTP protocol handler.
170 */
171
172const struct Curl_handler Curl_handler_sftp = {
173 "SFTP", /* scheme */
174 myssh_setup_connection, /* setup_connection */
175 myssh_do_it, /* do_it */
176 sftp_done, /* done */
177 ZERO_NULL, /* do_more */
178 myssh_connect, /* connect_it */
179 myssh_multi_statemach, /* connecting */
180 sftp_doing, /* doing */
181 myssh_getsock, /* proto_getsock */
182 myssh_getsock, /* doing_getsock */
183 ZERO_NULL, /* domore_getsock */
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700184 myssh_getsock, /* perform_getsock */
Elliott Hughes0128fe42018-02-27 14:57:55 -0800185 sftp_disconnect, /* disconnect */
186 ZERO_NULL, /* readwrite */
187 ZERO_NULL, /* connection_check */
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700188 ZERO_NULL, /* attach connection */
Elliott Hughes0128fe42018-02-27 14:57:55 -0800189 PORT_SSH, /* defport */
190 CURLPROTO_SFTP, /* protocol */
Haibo Huangb5a52b92020-10-28 22:18:23 -0700191 CURLPROTO_SFTP, /* family */
Elliott Hughes0128fe42018-02-27 14:57:55 -0800192 PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
193 | PROTOPT_NOURLQUERY /* flags */
194};
195
196static CURLcode sftp_error_to_CURLE(int err)
197{
198 switch(err) {
199 case SSH_FX_OK:
200 return CURLE_OK;
201
202 case SSH_FX_NO_SUCH_FILE:
203 case SSH_FX_NO_SUCH_PATH:
204 return CURLE_REMOTE_FILE_NOT_FOUND;
205
206 case SSH_FX_PERMISSION_DENIED:
207 case SSH_FX_WRITE_PROTECT:
208 return CURLE_REMOTE_ACCESS_DENIED;
209
210 case SSH_FX_FILE_ALREADY_EXISTS:
211 return CURLE_REMOTE_FILE_EXISTS;
212
213 default:
214 break;
215 }
216
217 return CURLE_SSH;
218}
219
Elliott Hughes72d948d2018-08-03 14:37:21 -0700220#ifndef DEBUGBUILD
221#define state(x,y) mystate(x,y)
222#else
223#define state(x,y) mystate(x,y, __LINE__)
224#endif
225
Elliott Hughes0128fe42018-02-27 14:57:55 -0800226/*
227 * SSH State machine related code
228 */
229/* This is the ONLY way to change SSH state! */
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700230static void mystate(struct Curl_easy *data, sshstate nowstate
Elliott Hughes72d948d2018-08-03 14:37:21 -0700231#ifdef DEBUGBUILD
232 , int lineno
233#endif
234 )
Elliott Hughes0128fe42018-02-27 14:57:55 -0800235{
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700236 struct connectdata *conn = data->conn;
Elliott Hughes0128fe42018-02-27 14:57:55 -0800237 struct ssh_conn *sshc = &conn->proto.sshc;
238#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
239 /* for debug purposes */
240 static const char *const names[] = {
241 "SSH_STOP",
242 "SSH_INIT",
243 "SSH_S_STARTUP",
244 "SSH_HOSTKEY",
245 "SSH_AUTHLIST",
246 "SSH_AUTH_PKEY_INIT",
247 "SSH_AUTH_PKEY",
248 "SSH_AUTH_PASS_INIT",
249 "SSH_AUTH_PASS",
250 "SSH_AUTH_AGENT_INIT",
251 "SSH_AUTH_AGENT_LIST",
252 "SSH_AUTH_AGENT",
253 "SSH_AUTH_HOST_INIT",
254 "SSH_AUTH_HOST",
255 "SSH_AUTH_KEY_INIT",
256 "SSH_AUTH_KEY",
257 "SSH_AUTH_GSSAPI",
258 "SSH_AUTH_DONE",
259 "SSH_SFTP_INIT",
260 "SSH_SFTP_REALPATH",
261 "SSH_SFTP_QUOTE_INIT",
262 "SSH_SFTP_POSTQUOTE_INIT",
263 "SSH_SFTP_QUOTE",
264 "SSH_SFTP_NEXT_QUOTE",
265 "SSH_SFTP_QUOTE_STAT",
266 "SSH_SFTP_QUOTE_SETSTAT",
267 "SSH_SFTP_QUOTE_SYMLINK",
268 "SSH_SFTP_QUOTE_MKDIR",
269 "SSH_SFTP_QUOTE_RENAME",
270 "SSH_SFTP_QUOTE_RMDIR",
271 "SSH_SFTP_QUOTE_UNLINK",
272 "SSH_SFTP_QUOTE_STATVFS",
273 "SSH_SFTP_GETINFO",
274 "SSH_SFTP_FILETIME",
275 "SSH_SFTP_TRANS_INIT",
276 "SSH_SFTP_UPLOAD_INIT",
277 "SSH_SFTP_CREATE_DIRS_INIT",
278 "SSH_SFTP_CREATE_DIRS",
279 "SSH_SFTP_CREATE_DIRS_MKDIR",
280 "SSH_SFTP_READDIR_INIT",
281 "SSH_SFTP_READDIR",
282 "SSH_SFTP_READDIR_LINK",
283 "SSH_SFTP_READDIR_BOTTOM",
284 "SSH_SFTP_READDIR_DONE",
285 "SSH_SFTP_DOWNLOAD_INIT",
286 "SSH_SFTP_DOWNLOAD_STAT",
287 "SSH_SFTP_CLOSE",
288 "SSH_SFTP_SHUTDOWN",
289 "SSH_SCP_TRANS_INIT",
290 "SSH_SCP_UPLOAD_INIT",
291 "SSH_SCP_DOWNLOAD_INIT",
292 "SSH_SCP_DOWNLOAD",
293 "SSH_SCP_DONE",
294 "SSH_SCP_SEND_EOF",
295 "SSH_SCP_WAIT_EOF",
296 "SSH_SCP_WAIT_CLOSE",
297 "SSH_SCP_CHANNEL_FREE",
298 "SSH_SESSION_DISCONNECT",
299 "SSH_SESSION_FREE",
300 "QUIT"
301 };
302
303
304 if(sshc->state != nowstate) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700305 infof(data, "SSH %p state change from %s to %s (line %d)",
Elliott Hughes72d948d2018-08-03 14:37:21 -0700306 (void *) sshc, names[sshc->state], names[nowstate],
307 lineno);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800308 }
309#endif
310
311 sshc->state = nowstate;
312}
313
314/* Multiple options:
315 * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5
316 * hash (90s style auth, not sure we should have it here)
317 * 2. data->set.ssh_keyfunc callback is set. Then we do trust on first
318 * use. We even save on knownhosts if CURLKHSTAT_FINE_ADD_TO_FILE
319 * is returned by it.
320 * 3. none of the above. We only accept if it is present on known hosts.
321 *
322 * Returns SSH_OK or SSH_ERROR.
323 */
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700324static int myssh_is_known(struct Curl_easy *data)
Elliott Hughes0128fe42018-02-27 14:57:55 -0800325{
326 int rc;
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700327 struct connectdata *conn = data->conn;
Elliott Hughes0128fe42018-02-27 14:57:55 -0800328 struct ssh_conn *sshc = &conn->proto.sshc;
329 ssh_key pubkey;
330 size_t hlen;
331 unsigned char *hash = NULL;
Haibo Huangb51266f2020-03-04 02:22:48 -0800332 char *found_base64 = NULL;
333 char *known_base64 = NULL;
Elliott Hughes0128fe42018-02-27 14:57:55 -0800334 int vstate;
335 enum curl_khmatch keymatch;
336 struct curl_khkey foundkey;
Haibo Huangb51266f2020-03-04 02:22:48 -0800337 struct curl_khkey *knownkeyp = NULL;
Elliott Hughes0128fe42018-02-27 14:57:55 -0800338 curl_sshkeycallback func =
339 data->set.ssh_keyfunc;
340
Haibo Huangb51266f2020-03-04 02:22:48 -0800341#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
342 struct ssh_knownhosts_entry *knownhostsentry = NULL;
343 struct curl_khkey knownkey;
344#endif
345
346#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0)
347 rc = ssh_get_server_publickey(sshc->ssh_session, &pubkey);
348#else
Elliott Hughes0128fe42018-02-27 14:57:55 -0800349 rc = ssh_get_publickey(sshc->ssh_session, &pubkey);
Haibo Huangb51266f2020-03-04 02:22:48 -0800350#endif
Elliott Hughes0128fe42018-02-27 14:57:55 -0800351 if(rc != SSH_OK)
352 return rc;
353
354 if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) {
Haibo Huangee03b1a2020-03-11 01:33:32 -0700355 int i;
356 char md5buffer[33];
357 const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5];
358
Elliott Hughes0128fe42018-02-27 14:57:55 -0800359 rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5,
360 &hash, &hlen);
Haibo Huangee03b1a2020-03-11 01:33:32 -0700361 if(rc != SSH_OK || hlen != 16) {
362 failf(data,
363 "Denied establishing ssh session: md5 fingerprint not available");
Elliott Hughes0128fe42018-02-27 14:57:55 -0800364 goto cleanup;
Haibo Huangee03b1a2020-03-11 01:33:32 -0700365 }
Elliott Hughes0128fe42018-02-27 14:57:55 -0800366
Haibo Huangee03b1a2020-03-11 01:33:32 -0700367 for(i = 0; i < 16; i++)
368 msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char)hash[i]);
369
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700370 infof(data, "SSH MD5 fingerprint: %s", md5buffer);
Haibo Huangee03b1a2020-03-11 01:33:32 -0700371
372 if(!strcasecompare(md5buffer, pubkey_md5)) {
373 failf(data,
374 "Denied establishing ssh session: mismatch md5 fingerprint. "
375 "Remote %s is not equal to %s", md5buffer, pubkey_md5);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800376 rc = SSH_ERROR;
377 goto cleanup;
378 }
379
380 rc = SSH_OK;
381 goto cleanup;
382 }
383
384 if(data->set.ssl.primary.verifyhost != TRUE) {
385 rc = SSH_OK;
386 goto cleanup;
387 }
388
Haibo Huangb51266f2020-03-04 02:22:48 -0800389#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
390 /* Get the known_key from the known hosts file */
391 vstate = ssh_session_get_known_hosts_entry(sshc->ssh_session,
392 &knownhostsentry);
393
394 /* Case an entry was found in a known hosts file */
395 if(knownhostsentry) {
396 if(knownhostsentry->publickey) {
397 rc = ssh_pki_export_pubkey_base64(knownhostsentry->publickey,
398 &known_base64);
399 if(rc != SSH_OK) {
400 goto cleanup;
401 }
402 knownkey.key = known_base64;
403 knownkey.len = strlen(known_base64);
404
405 switch(ssh_key_type(knownhostsentry->publickey)) {
406 case SSH_KEYTYPE_RSA:
407 knownkey.keytype = CURLKHTYPE_RSA;
408 break;
409 case SSH_KEYTYPE_RSA1:
410 knownkey.keytype = CURLKHTYPE_RSA1;
411 break;
412 case SSH_KEYTYPE_ECDSA:
Haibo Huang24c77a12020-04-29 13:49:57 -0700413 case SSH_KEYTYPE_ECDSA_P256:
414 case SSH_KEYTYPE_ECDSA_P384:
415 case SSH_KEYTYPE_ECDSA_P521:
Haibo Huangb51266f2020-03-04 02:22:48 -0800416 knownkey.keytype = CURLKHTYPE_ECDSA;
417 break;
418 case SSH_KEYTYPE_ED25519:
419 knownkey.keytype = CURLKHTYPE_ED25519;
420 break;
421 case SSH_KEYTYPE_DSS:
422 knownkey.keytype = CURLKHTYPE_DSS;
423 break;
424 default:
425 rc = SSH_ERROR;
426 goto cleanup;
427 }
428 knownkeyp = &knownkey;
429 }
430 }
431
432 switch(vstate) {
433 case SSH_KNOWN_HOSTS_OK:
434 keymatch = CURLKHMATCH_OK;
435 break;
436 case SSH_KNOWN_HOSTS_OTHER:
437 /* fallthrough */
438 case SSH_KNOWN_HOSTS_NOT_FOUND:
439 /* fallthrough */
440 case SSH_KNOWN_HOSTS_UNKNOWN:
441 /* fallthrough */
442 case SSH_KNOWN_HOSTS_ERROR:
443 keymatch = CURLKHMATCH_MISSING;
444 break;
445 default:
446 keymatch = CURLKHMATCH_MISMATCH;
447 break;
448 }
449
450#else
Elliott Hughes0128fe42018-02-27 14:57:55 -0800451 vstate = ssh_is_server_known(sshc->ssh_session);
452 switch(vstate) {
453 case SSH_SERVER_KNOWN_OK:
454 keymatch = CURLKHMATCH_OK;
455 break;
456 case SSH_SERVER_FILE_NOT_FOUND:
457 /* fallthrough */
458 case SSH_SERVER_NOT_KNOWN:
459 keymatch = CURLKHMATCH_MISSING;
460 break;
461 default:
462 keymatch = CURLKHMATCH_MISMATCH;
463 break;
464 }
Haibo Huangb51266f2020-03-04 02:22:48 -0800465#endif
Elliott Hughes0128fe42018-02-27 14:57:55 -0800466
467 if(func) { /* use callback to determine action */
Haibo Huangb51266f2020-03-04 02:22:48 -0800468 rc = ssh_pki_export_pubkey_base64(pubkey, &found_base64);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800469 if(rc != SSH_OK)
470 goto cleanup;
471
Haibo Huangb51266f2020-03-04 02:22:48 -0800472 foundkey.key = found_base64;
473 foundkey.len = strlen(found_base64);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800474
475 switch(ssh_key_type(pubkey)) {
476 case SSH_KEYTYPE_RSA:
477 foundkey.keytype = CURLKHTYPE_RSA;
478 break;
479 case SSH_KEYTYPE_RSA1:
480 foundkey.keytype = CURLKHTYPE_RSA1;
481 break;
482 case SSH_KEYTYPE_ECDSA:
Haibo Huang24c77a12020-04-29 13:49:57 -0700483#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
484 case SSH_KEYTYPE_ECDSA_P256:
485 case SSH_KEYTYPE_ECDSA_P384:
486 case SSH_KEYTYPE_ECDSA_P521:
487#endif
Elliott Hughes0128fe42018-02-27 14:57:55 -0800488 foundkey.keytype = CURLKHTYPE_ECDSA;
489 break;
490#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,7,0)
491 case SSH_KEYTYPE_ED25519:
492 foundkey.keytype = CURLKHTYPE_ED25519;
493 break;
494#endif
495 case SSH_KEYTYPE_DSS:
496 foundkey.keytype = CURLKHTYPE_DSS;
497 break;
498 default:
499 rc = SSH_ERROR;
500 goto cleanup;
501 }
502
Elliott Hughescac39802018-04-27 16:19:43 -0700503 Curl_set_in_callback(data, true);
Haibo Huangb51266f2020-03-04 02:22:48 -0800504 rc = func(data, knownkeyp, /* from the knownhosts file */
505 &foundkey, /* from the remote host */
Elliott Hughes0128fe42018-02-27 14:57:55 -0800506 keymatch, data->set.ssh_keyfunc_userp);
Elliott Hughescac39802018-04-27 16:19:43 -0700507 Curl_set_in_callback(data, false);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800508
509 switch(rc) {
510 case CURLKHSTAT_FINE_ADD_TO_FILE:
Haibo Huangb51266f2020-03-04 02:22:48 -0800511#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0)
512 rc = ssh_session_update_known_hosts(sshc->ssh_session);
513#else
Elliott Hughes0128fe42018-02-27 14:57:55 -0800514 rc = ssh_write_knownhost(sshc->ssh_session);
Haibo Huangb51266f2020-03-04 02:22:48 -0800515#endif
Elliott Hughes0128fe42018-02-27 14:57:55 -0800516 if(rc != SSH_OK) {
517 goto cleanup;
518 }
519 break;
520 case CURLKHSTAT_FINE:
521 break;
522 default: /* REJECT/DEFER */
523 rc = SSH_ERROR;
524 goto cleanup;
525 }
526 }
527 else {
528 if(keymatch != CURLKHMATCH_OK) {
529 rc = SSH_ERROR;
530 goto cleanup;
531 }
532 }
533 rc = SSH_OK;
534
535cleanup:
Haibo Huangb51266f2020-03-04 02:22:48 -0800536 if(found_base64) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700537 (free)(found_base64);
Haibo Huangb51266f2020-03-04 02:22:48 -0800538 }
539 if(known_base64) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700540 (free)(known_base64);
Haibo Huangb51266f2020-03-04 02:22:48 -0800541 }
Elliott Hughes0128fe42018-02-27 14:57:55 -0800542 if(hash)
543 ssh_clean_pubkey_hash(&hash);
544 ssh_key_free(pubkey);
Haibo Huangb51266f2020-03-04 02:22:48 -0800545#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
546 if(knownhostsentry) {
547 ssh_knownhosts_entry_free(knownhostsentry);
548 }
549#endif
Elliott Hughes0128fe42018-02-27 14:57:55 -0800550 return rc;
551}
552
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700553#define MOVE_TO_ERROR_STATE(_r) do { \
554 state(data, SSH_SESSION_DISCONNECT); \
555 sshc->actualcode = _r; \
556 rc = SSH_ERROR; \
557 } while(0)
Elliott Hughes0128fe42018-02-27 14:57:55 -0800558
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700559#define MOVE_TO_SFTP_CLOSE_STATE() do { \
560 state(data, SSH_SFTP_CLOSE); \
561 sshc->actualcode = \
562 sftp_error_to_CURLE(sftp_get_error(sshc->sftp_session)); \
563 rc = SSH_ERROR; \
564 } while(0)
Elliott Hughes0128fe42018-02-27 14:57:55 -0800565
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700566#define MOVE_TO_LAST_AUTH do { \
567 if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { \
568 rc = SSH_OK; \
569 state(data, SSH_AUTH_PASS_INIT); \
570 } \
571 else { \
572 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); \
573 } \
574 } while(0)
Elliott Hughes0128fe42018-02-27 14:57:55 -0800575
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700576#define MOVE_TO_TERTIARY_AUTH do { \
577 if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { \
578 rc = SSH_OK; \
579 state(data, SSH_AUTH_KEY_INIT); \
580 } \
581 else { \
582 MOVE_TO_LAST_AUTH; \
583 } \
584 } while(0)
Elliott Hughes0128fe42018-02-27 14:57:55 -0800585
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700586#define MOVE_TO_SECONDARY_AUTH do { \
587 if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { \
588 rc = SSH_OK; \
589 state(data, SSH_AUTH_GSSAPI); \
590 } \
591 else { \
592 MOVE_TO_TERTIARY_AUTH; \
593 } \
594 } while(0)
Elliott Hughes0128fe42018-02-27 14:57:55 -0800595
596static
597int myssh_auth_interactive(struct connectdata *conn)
598{
599 int rc;
600 struct ssh_conn *sshc = &conn->proto.sshc;
601 int nprompts;
602
603restart:
604 switch(sshc->kbd_state) {
605 case 0:
606 rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL);
607 if(rc == SSH_AUTH_AGAIN)
608 return SSH_AGAIN;
609
610 if(rc != SSH_AUTH_INFO)
611 return SSH_ERROR;
612
613 nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session);
Haibo Huang31944072019-11-06 02:28:57 -0800614 if(nprompts != 1)
Elliott Hughes0128fe42018-02-27 14:57:55 -0800615 return SSH_ERROR;
616
617 rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, conn->passwd);
618 if(rc < 0)
619 return SSH_ERROR;
620
Elliott Hughesb1ef70f2018-10-30 11:28:38 -0700621 /* FALLTHROUGH */
Elliott Hughes0128fe42018-02-27 14:57:55 -0800622 case 1:
623 sshc->kbd_state = 1;
624
625 rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL);
626 if(rc == SSH_AUTH_AGAIN)
627 return SSH_AGAIN;
628 else if(rc == SSH_AUTH_SUCCESS)
629 rc = SSH_OK;
630 else if(rc == SSH_AUTH_INFO) {
631 nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session);
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700632 if(nprompts)
Elliott Hughes0128fe42018-02-27 14:57:55 -0800633 return SSH_ERROR;
634
635 sshc->kbd_state = 2;
636 goto restart;
637 }
638 else
639 rc = SSH_ERROR;
640 break;
641 case 2:
642 sshc->kbd_state = 2;
643
644 rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL);
645 if(rc == SSH_AUTH_AGAIN)
646 return SSH_AGAIN;
647 else if(rc == SSH_AUTH_SUCCESS)
648 rc = SSH_OK;
649 else
650 rc = SSH_ERROR;
651
652 break;
653 default:
654 return SSH_ERROR;
655 }
656
657 sshc->kbd_state = 0;
658 return rc;
659}
660
661/*
662 * ssh_statemach_act() runs the SSH state machine as far as it can without
663 * blocking and without reaching the end. The data the pointer 'block' points
664 * to will be set to TRUE if the libssh function returns SSH_AGAIN
665 * meaning it wants to be called again when the socket is ready
666 */
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700667static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
Elliott Hughes0128fe42018-02-27 14:57:55 -0800668{
669 CURLcode result = CURLE_OK;
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700670 struct connectdata *conn = data->conn;
671 struct SSHPROTO *protop = data->req.p.ssh;
Elliott Hughes0128fe42018-02-27 14:57:55 -0800672 struct ssh_conn *sshc = &conn->proto.sshc;
Haibo Huang51d9d882019-02-06 01:36:06 -0800673 curl_socket_t sock = conn->sock[FIRSTSOCKET];
Elliott Hughes0128fe42018-02-27 14:57:55 -0800674 int rc = SSH_NO_ERROR, err;
675 char *new_readdir_line;
676 int seekerr = CURL_SEEKFUNC_OK;
677 const char *err_msg;
678 *block = 0; /* we're not blocking by default */
679
680 do {
681
682 switch(sshc->state) {
683 case SSH_INIT:
684 sshc->secondCreateDirs = 0;
685 sshc->nextstate = SSH_NO_STATE;
686 sshc->actualcode = CURLE_OK;
687
688#if 0
689 ssh_set_log_level(SSH_LOG_PROTOCOL);
690#endif
691
692 /* Set libssh to non-blocking, since everything internally is
693 non-blocking */
694 ssh_set_blocking(sshc->ssh_session, 0);
695
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700696 state(data, SSH_S_STARTUP);
Elliott Hughesb1ef70f2018-10-30 11:28:38 -0700697 /* FALLTHROUGH */
Elliott Hughes0128fe42018-02-27 14:57:55 -0800698
699 case SSH_S_STARTUP:
700 rc = ssh_connect(sshc->ssh_session);
701 if(rc == SSH_AGAIN)
702 break;
703
704 if(rc != SSH_OK) {
705 failf(data, "Failure establishing ssh session");
706 MOVE_TO_ERROR_STATE(CURLE_FAILED_INIT);
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700707 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -0800708 }
709
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700710 state(data, SSH_HOSTKEY);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800711
Elliott Hughesb1ef70f2018-10-30 11:28:38 -0700712 /* FALLTHROUGH */
Elliott Hughes0128fe42018-02-27 14:57:55 -0800713 case SSH_HOSTKEY:
714
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700715 rc = myssh_is_known(data);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800716 if(rc != SSH_OK) {
717 MOVE_TO_ERROR_STATE(CURLE_PEER_FAILED_VERIFICATION);
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700718 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -0800719 }
720
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700721 state(data, SSH_AUTHLIST);
Elliott Hughesb1ef70f2018-10-30 11:28:38 -0700722 /* FALLTHROUGH */
Elliott Hughes0128fe42018-02-27 14:57:55 -0800723 case SSH_AUTHLIST:{
724 sshc->authed = FALSE;
725
726 rc = ssh_userauth_none(sshc->ssh_session, NULL);
727 if(rc == SSH_AUTH_AGAIN) {
728 rc = SSH_AGAIN;
729 break;
730 }
731
732 if(rc == SSH_AUTH_SUCCESS) {
733 sshc->authed = TRUE;
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700734 infof(data, "Authenticated with none");
735 state(data, SSH_AUTH_DONE);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800736 break;
737 }
738 else if(rc == SSH_AUTH_ERROR) {
739 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700740 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -0800741 }
742
743 sshc->auth_methods = ssh_userauth_list(sshc->ssh_session, NULL);
744 if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700745 state(data, SSH_AUTH_PKEY_INIT);
746 infof(data, "Authentication using SSH public key file");
Elliott Hughes0128fe42018-02-27 14:57:55 -0800747 }
748 else if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700749 state(data, SSH_AUTH_GSSAPI);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800750 }
751 else if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700752 state(data, SSH_AUTH_KEY_INIT);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800753 }
754 else if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700755 state(data, SSH_AUTH_PASS_INIT);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800756 }
757 else { /* unsupported authentication method */
758 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700759 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -0800760 }
761
762 break;
763 }
764 case SSH_AUTH_PKEY_INIT:
765 if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY)) {
766 MOVE_TO_SECONDARY_AUTH;
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700767 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -0800768 }
769
770 /* Two choices, (1) private key was given on CMD,
771 * (2) use the "default" keys. */
772 if(data->set.str[STRING_SSH_PRIVATE_KEY]) {
773 if(sshc->pubkey && !data->set.ssl.key_passwd) {
774 rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL,
775 sshc->pubkey);
776 if(rc == SSH_AUTH_AGAIN) {
777 rc = SSH_AGAIN;
778 break;
779 }
780
781 if(rc != SSH_OK) {
782 MOVE_TO_SECONDARY_AUTH;
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700783 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -0800784 }
785 }
786
787 rc = ssh_pki_import_privkey_file(data->
788 set.str[STRING_SSH_PRIVATE_KEY],
789 data->set.ssl.key_passwd, NULL,
790 NULL, &sshc->privkey);
791 if(rc != SSH_OK) {
792 failf(data, "Could not load private key file %s",
793 data->set.str[STRING_SSH_PRIVATE_KEY]);
Elliott Hughesb1ef70f2018-10-30 11:28:38 -0700794 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800795 break;
796 }
797
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700798 state(data, SSH_AUTH_PKEY);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800799 break;
800
801 }
802 else {
Elliott Hughes0128fe42018-02-27 14:57:55 -0800803 rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL,
804 data->set.ssl.key_passwd);
805 if(rc == SSH_AUTH_AGAIN) {
806 rc = SSH_AGAIN;
807 break;
808 }
809 if(rc == SSH_AUTH_SUCCESS) {
810 rc = SSH_OK;
811 sshc->authed = TRUE;
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700812 infof(data, "Completed public key authentication");
813 state(data, SSH_AUTH_DONE);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800814 break;
815 }
816
817 MOVE_TO_SECONDARY_AUTH;
818 }
819 break;
820 case SSH_AUTH_PKEY:
821 rc = ssh_userauth_publickey(sshc->ssh_session, NULL, sshc->privkey);
822 if(rc == SSH_AUTH_AGAIN) {
823 rc = SSH_AGAIN;
824 break;
825 }
826
827 if(rc == SSH_AUTH_SUCCESS) {
828 sshc->authed = TRUE;
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700829 infof(data, "Completed public key authentication");
830 state(data, SSH_AUTH_DONE);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800831 break;
832 }
833 else {
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700834 infof(data, "Failed public key authentication (rc: %d)", rc);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800835 MOVE_TO_SECONDARY_AUTH;
836 }
837 break;
838
839 case SSH_AUTH_GSSAPI:
840 if(!(data->set.ssh_auth_types & CURLSSH_AUTH_GSSAPI)) {
841 MOVE_TO_TERTIARY_AUTH;
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700842 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -0800843 }
844
845 rc = ssh_userauth_gssapi(sshc->ssh_session);
846 if(rc == SSH_AUTH_AGAIN) {
847 rc = SSH_AGAIN;
848 break;
849 }
850
851 if(rc == SSH_AUTH_SUCCESS) {
852 rc = SSH_OK;
853 sshc->authed = TRUE;
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700854 infof(data, "Completed gssapi authentication");
855 state(data, SSH_AUTH_DONE);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800856 break;
857 }
858
859 MOVE_TO_TERTIARY_AUTH;
860 break;
861
862 case SSH_AUTH_KEY_INIT:
863 if(data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700864 state(data, SSH_AUTH_KEY);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800865 }
866 else {
867 MOVE_TO_LAST_AUTH;
868 }
869 break;
870
871 case SSH_AUTH_KEY:
872
873 /* Authentication failed. Continue with keyboard-interactive now. */
874 rc = myssh_auth_interactive(conn);
875 if(rc == SSH_AGAIN) {
876 break;
877 }
878 if(rc == SSH_OK) {
879 sshc->authed = TRUE;
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700880 infof(data, "completed keyboard interactive authentication");
Elliott Hughes0128fe42018-02-27 14:57:55 -0800881 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700882 state(data, SSH_AUTH_DONE);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800883 break;
884
885 case SSH_AUTH_PASS_INIT:
886 if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD)) {
887 /* Host key authentication is intentionally not implemented */
888 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700889 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -0800890 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700891 state(data, SSH_AUTH_PASS);
Elliott Hughesb1ef70f2018-10-30 11:28:38 -0700892 /* FALLTHROUGH */
Elliott Hughes0128fe42018-02-27 14:57:55 -0800893
894 case SSH_AUTH_PASS:
895 rc = ssh_userauth_password(sshc->ssh_session, NULL, conn->passwd);
896 if(rc == SSH_AUTH_AGAIN) {
897 rc = SSH_AGAIN;
898 break;
899 }
900
901 if(rc == SSH_AUTH_SUCCESS) {
902 sshc->authed = TRUE;
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700903 infof(data, "Completed password authentication");
904 state(data, SSH_AUTH_DONE);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800905 }
906 else {
907 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
908 }
909 break;
910
911 case SSH_AUTH_DONE:
912 if(!sshc->authed) {
913 failf(data, "Authentication failure");
914 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
915 break;
916 }
917
918 /*
919 * At this point we have an authenticated ssh session.
920 */
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700921 infof(data, "Authentication complete");
Elliott Hughes0128fe42018-02-27 14:57:55 -0800922
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700923 Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */
Elliott Hughes0128fe42018-02-27 14:57:55 -0800924
Haibo Huang51d9d882019-02-06 01:36:06 -0800925 conn->sockfd = sock;
Elliott Hughes0128fe42018-02-27 14:57:55 -0800926 conn->writesockfd = CURL_SOCKET_BAD;
927
928 if(conn->handler->protocol == CURLPROTO_SFTP) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700929 state(data, SSH_SFTP_INIT);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800930 break;
931 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700932 infof(data, "SSH CONNECT phase done");
933 state(data, SSH_STOP);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800934 break;
935
936 case SSH_SFTP_INIT:
937 ssh_set_blocking(sshc->ssh_session, 1);
938
939 sshc->sftp_session = sftp_new(sshc->ssh_session);
940 if(!sshc->sftp_session) {
941 failf(data, "Failure initializing sftp session: %s",
942 ssh_get_error(sshc->ssh_session));
943 MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
944 break;
945 }
946
947 rc = sftp_init(sshc->sftp_session);
948 if(rc != SSH_OK) {
949 rc = sftp_get_error(sshc->sftp_session);
950 failf(data, "Failure initializing sftp session: %s",
951 ssh_get_error(sshc->ssh_session));
952 MOVE_TO_ERROR_STATE(sftp_error_to_CURLE(rc));
953 break;
954 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700955 state(data, SSH_SFTP_REALPATH);
Elliott Hughesb1ef70f2018-10-30 11:28:38 -0700956 /* FALLTHROUGH */
Elliott Hughes0128fe42018-02-27 14:57:55 -0800957 case SSH_SFTP_REALPATH:
958 /*
959 * Get the "home" directory
960 */
961 sshc->homedir = sftp_canonicalize_path(sshc->sftp_session, ".");
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700962 if(!sshc->homedir) {
Elliott Hughes0128fe42018-02-27 14:57:55 -0800963 MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700964 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -0800965 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700966 data->state.most_recent_ftp_entrypath = sshc->homedir;
Elliott Hughes0128fe42018-02-27 14:57:55 -0800967
968 /* This is the last step in the SFTP connect phase. Do note that while
969 we get the homedir here, we get the "workingpath" in the DO action
970 since the homedir will remain the same between request but the
971 working path will not. */
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700972 DEBUGF(infof(data, "SSH CONNECT phase done"));
973 state(data, SSH_STOP);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800974 break;
975
976 case SSH_SFTP_QUOTE_INIT:
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700977 result = Curl_getworkingpath(data, sshc->homedir, &protop->path);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800978 if(result) {
979 sshc->actualcode = result;
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700980 state(data, SSH_STOP);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800981 break;
982 }
983
984 if(data->set.quote) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700985 infof(data, "Sending quote commands");
Elliott Hughes0128fe42018-02-27 14:57:55 -0800986 sshc->quote_item = data->set.quote;
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700987 state(data, SSH_SFTP_QUOTE);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800988 }
989 else {
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700990 state(data, SSH_SFTP_GETINFO);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800991 }
992 break;
993
994 case SSH_SFTP_POSTQUOTE_INIT:
995 if(data->set.postquote) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700996 infof(data, "Sending quote commands");
Elliott Hughes0128fe42018-02-27 14:57:55 -0800997 sshc->quote_item = data->set.postquote;
Elliott Hughes34dd5f42021-08-10 13:01:18 -0700998 state(data, SSH_SFTP_QUOTE);
Elliott Hughes0128fe42018-02-27 14:57:55 -0800999 }
1000 else {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001001 state(data, SSH_STOP);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001002 }
1003 break;
1004
1005 case SSH_SFTP_QUOTE:
1006 /* Send any quote commands */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001007 sftp_quote(data);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001008 break;
1009
1010 case SSH_SFTP_NEXT_QUOTE:
1011 Curl_safefree(sshc->quote_path1);
1012 Curl_safefree(sshc->quote_path2);
1013
1014 sshc->quote_item = sshc->quote_item->next;
1015
1016 if(sshc->quote_item) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001017 state(data, SSH_SFTP_QUOTE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001018 }
1019 else {
1020 if(sshc->nextstate != SSH_NO_STATE) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001021 state(data, sshc->nextstate);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001022 sshc->nextstate = SSH_NO_STATE;
1023 }
1024 else {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001025 state(data, SSH_SFTP_GETINFO);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001026 }
1027 }
1028 break;
1029
1030 case SSH_SFTP_QUOTE_STAT:
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001031 sftp_quote_stat(data);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001032 break;
1033
1034 case SSH_SFTP_QUOTE_SETSTAT:
1035 rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2,
1036 sshc->quote_attrs);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001037 if(rc && !sshc->acceptfail) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08001038 Curl_safefree(sshc->quote_path1);
1039 Curl_safefree(sshc->quote_path2);
1040 failf(data, "Attempt to set SFTP stats failed: %s",
1041 ssh_get_error(sshc->ssh_session));
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001042 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001043 sshc->nextstate = SSH_NO_STATE;
1044 sshc->actualcode = CURLE_QUOTE_ERROR;
1045 /* sshc->actualcode = sftp_error_to_CURLE(err);
1046 * we do not send the actual error; we return
1047 * the error the libssh2 backend is returning */
1048 break;
1049 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001050 state(data, SSH_SFTP_NEXT_QUOTE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001051 break;
1052
1053 case SSH_SFTP_QUOTE_SYMLINK:
1054 rc = sftp_symlink(sshc->sftp_session, sshc->quote_path2,
1055 sshc->quote_path1);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001056 if(rc && !sshc->acceptfail) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08001057 Curl_safefree(sshc->quote_path1);
1058 Curl_safefree(sshc->quote_path2);
1059 failf(data, "symlink command failed: %s",
1060 ssh_get_error(sshc->ssh_session));
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001061 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001062 sshc->nextstate = SSH_NO_STATE;
1063 sshc->actualcode = CURLE_QUOTE_ERROR;
1064 break;
1065 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001066 state(data, SSH_SFTP_NEXT_QUOTE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001067 break;
1068
1069 case SSH_SFTP_QUOTE_MKDIR:
1070 rc = sftp_mkdir(sshc->sftp_session, sshc->quote_path1,
1071 (mode_t)data->set.new_directory_perms);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001072 if(rc && !sshc->acceptfail) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08001073 Curl_safefree(sshc->quote_path1);
1074 failf(data, "mkdir command failed: %s",
1075 ssh_get_error(sshc->ssh_session));
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001076 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001077 sshc->nextstate = SSH_NO_STATE;
1078 sshc->actualcode = CURLE_QUOTE_ERROR;
1079 break;
1080 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001081 state(data, SSH_SFTP_NEXT_QUOTE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001082 break;
1083
1084 case SSH_SFTP_QUOTE_RENAME:
1085 rc = sftp_rename(sshc->sftp_session, sshc->quote_path1,
1086 sshc->quote_path2);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001087 if(rc && !sshc->acceptfail) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08001088 Curl_safefree(sshc->quote_path1);
1089 Curl_safefree(sshc->quote_path2);
1090 failf(data, "rename command failed: %s",
1091 ssh_get_error(sshc->ssh_session));
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001092 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001093 sshc->nextstate = SSH_NO_STATE;
1094 sshc->actualcode = CURLE_QUOTE_ERROR;
1095 break;
1096 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001097 state(data, SSH_SFTP_NEXT_QUOTE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001098 break;
1099
1100 case SSH_SFTP_QUOTE_RMDIR:
1101 rc = sftp_rmdir(sshc->sftp_session, sshc->quote_path1);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001102 if(rc && !sshc->acceptfail) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08001103 Curl_safefree(sshc->quote_path1);
1104 failf(data, "rmdir command failed: %s",
1105 ssh_get_error(sshc->ssh_session));
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001106 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001107 sshc->nextstate = SSH_NO_STATE;
1108 sshc->actualcode = CURLE_QUOTE_ERROR;
1109 break;
1110 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001111 state(data, SSH_SFTP_NEXT_QUOTE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001112 break;
1113
1114 case SSH_SFTP_QUOTE_UNLINK:
1115 rc = sftp_unlink(sshc->sftp_session, sshc->quote_path1);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001116 if(rc && !sshc->acceptfail) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08001117 Curl_safefree(sshc->quote_path1);
1118 failf(data, "rm command failed: %s",
1119 ssh_get_error(sshc->ssh_session));
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001120 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001121 sshc->nextstate = SSH_NO_STATE;
1122 sshc->actualcode = CURLE_QUOTE_ERROR;
1123 break;
1124 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001125 state(data, SSH_SFTP_NEXT_QUOTE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001126 break;
1127
1128 case SSH_SFTP_QUOTE_STATVFS:
1129 {
1130 sftp_statvfs_t statvfs;
1131
1132 statvfs = sftp_statvfs(sshc->sftp_session, sshc->quote_path1);
1133 if(!statvfs && !sshc->acceptfail) {
1134 Curl_safefree(sshc->quote_path1);
1135 failf(data, "statvfs command failed: %s",
1136 ssh_get_error(sshc->ssh_session));
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001137 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001138 sshc->nextstate = SSH_NO_STATE;
1139 sshc->actualcode = CURLE_QUOTE_ERROR;
1140 break;
1141 }
1142 else if(statvfs) {
1143 char *tmp = aprintf("statvfs:\n"
1144 "f_bsize: %llu\n" "f_frsize: %llu\n"
1145 "f_blocks: %llu\n" "f_bfree: %llu\n"
1146 "f_bavail: %llu\n" "f_files: %llu\n"
1147 "f_ffree: %llu\n" "f_favail: %llu\n"
1148 "f_fsid: %llu\n" "f_flag: %llu\n"
1149 "f_namemax: %llu\n",
1150 statvfs->f_bsize, statvfs->f_frsize,
1151 statvfs->f_blocks, statvfs->f_bfree,
1152 statvfs->f_bavail, statvfs->f_files,
1153 statvfs->f_ffree, statvfs->f_favail,
1154 statvfs->f_fsid, statvfs->f_flag,
1155 statvfs->f_namemax);
1156 sftp_statvfs_free(statvfs);
1157
1158 if(!tmp) {
1159 result = CURLE_OUT_OF_MEMORY;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001160 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001161 sshc->nextstate = SSH_NO_STATE;
1162 break;
1163 }
1164
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001165 result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
Elliott Hughes0128fe42018-02-27 14:57:55 -08001166 free(tmp);
1167 if(result) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001168 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001169 sshc->nextstate = SSH_NO_STATE;
1170 sshc->actualcode = result;
1171 }
1172 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001173 state(data, SSH_SFTP_NEXT_QUOTE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001174 break;
1175 }
1176
1177 case SSH_SFTP_GETINFO:
1178 if(data->set.get_filetime) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001179 state(data, SSH_SFTP_FILETIME);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001180 }
1181 else {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001182 state(data, SSH_SFTP_TRANS_INIT);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001183 }
1184 break;
1185
1186 case SSH_SFTP_FILETIME:
1187 {
1188 sftp_attributes attrs;
1189
1190 attrs = sftp_stat(sshc->sftp_session, protop->path);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001191 if(attrs) {
Elliott Hughescac39802018-04-27 16:19:43 -07001192 data->info.filetime = attrs->mtime;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001193 sftp_attributes_free(attrs);
1194 }
1195
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001196 state(data, SSH_SFTP_TRANS_INIT);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001197 break;
1198 }
1199
1200 case SSH_SFTP_TRANS_INIT:
1201 if(data->set.upload)
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001202 state(data, SSH_SFTP_UPLOAD_INIT);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001203 else {
1204 if(protop->path[strlen(protop->path)-1] == '/')
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001205 state(data, SSH_SFTP_READDIR_INIT);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001206 else
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001207 state(data, SSH_SFTP_DOWNLOAD_INIT);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001208 }
1209 break;
1210
1211 case SSH_SFTP_UPLOAD_INIT:
1212 {
1213 int flags;
1214
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001215 if(data->state.resume_from) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08001216 sftp_attributes attrs;
1217
1218 if(data->state.resume_from < 0) {
1219 attrs = sftp_stat(sshc->sftp_session, protop->path);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001220 if(attrs) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08001221 curl_off_t size = attrs->size;
1222 if(size < 0) {
1223 failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
1224 MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001225 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001226 }
1227 data->state.resume_from = attrs->size;
1228
1229 sftp_attributes_free(attrs);
1230 }
1231 else {
1232 data->state.resume_from = 0;
1233 }
1234 }
1235 }
1236
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001237 if(data->set.remote_append)
Elliott Hughes0128fe42018-02-27 14:57:55 -08001238 /* Try to open for append, but create if nonexisting */
1239 flags = O_WRONLY|O_CREAT|O_APPEND;
1240 else if(data->state.resume_from > 0)
1241 /* If we have restart position then open for append */
1242 flags = O_WRONLY|O_APPEND;
1243 else
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001244 /* Clear file before writing (normal behavior) */
Haibo Huang445085a2019-09-11 13:33:50 -07001245 flags = O_WRONLY|O_CREAT|O_TRUNC;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001246
1247 if(sshc->sftp_file)
1248 sftp_close(sshc->sftp_file);
1249 sshc->sftp_file =
1250 sftp_open(sshc->sftp_session, protop->path,
1251 flags, (mode_t)data->set.new_file_perms);
1252 if(!sshc->sftp_file) {
1253 err = sftp_get_error(sshc->sftp_session);
1254
1255 if(((err == SSH_FX_NO_SUCH_FILE || err == SSH_FX_FAILURE ||
1256 err == SSH_FX_NO_SUCH_PATH)) &&
1257 (data->set.ftp_create_missing_dirs &&
1258 (strlen(protop->path) > 1))) {
1259 /* try to create the path remotely */
1260 rc = 0;
1261 sshc->secondCreateDirs = 1;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001262 state(data, SSH_SFTP_CREATE_DIRS_INIT);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001263 break;
1264 }
1265 else {
1266 MOVE_TO_SFTP_CLOSE_STATE();
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001267 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001268 }
1269 }
1270
1271 /* If we have a restart point then we need to seek to the correct
1272 position. */
1273 if(data->state.resume_from > 0) {
1274 /* Let's read off the proper amount of bytes from the input. */
1275 if(conn->seek_func) {
Elliott Hughescac39802018-04-27 16:19:43 -07001276 Curl_set_in_callback(data, true);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001277 seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
1278 SEEK_SET);
Elliott Hughescac39802018-04-27 16:19:43 -07001279 Curl_set_in_callback(data, false);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001280 }
1281
1282 if(seekerr != CURL_SEEKFUNC_OK) {
1283 curl_off_t passed = 0;
1284
1285 if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
1286 failf(data, "Could not seek stream");
1287 return CURLE_FTP_COULDNT_USE_REST;
1288 }
1289 /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
1290 do {
1291 size_t readthisamountnow =
1292 (data->state.resume_from - passed > data->set.buffer_size) ?
1293 (size_t)data->set.buffer_size :
1294 curlx_sotouz(data->state.resume_from - passed);
1295
1296 size_t actuallyread =
1297 data->state.fread_func(data->state.buffer, 1,
1298 readthisamountnow, data->state.in);
1299
1300 passed += actuallyread;
1301 if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
1302 /* this checks for greater-than only to make sure that the
1303 CURL_READFUNC_ABORT return code still aborts */
1304 failf(data, "Failed to read data");
1305 MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001306 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001307 }
1308 } while(passed < data->state.resume_from);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001309 if(rc)
1310 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001311 }
1312
1313 /* now, decrease the size of the read */
1314 if(data->state.infilesize > 0) {
1315 data->state.infilesize -= data->state.resume_from;
1316 data->req.size = data->state.infilesize;
1317 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1318 }
1319
1320 rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001321 if(rc) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08001322 MOVE_TO_SFTP_CLOSE_STATE();
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001323 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001324 }
1325 }
1326 if(data->state.infilesize > 0) {
1327 data->req.size = data->state.infilesize;
1328 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1329 }
1330 /* upload data */
Haibo Huang65021c72019-03-27 15:37:23 -07001331 Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001332
1333 /* not set by Curl_setup_transfer to preserve keepon bits */
1334 conn->sockfd = conn->writesockfd;
1335
1336 /* store this original bitmask setup to use later on if we can't
1337 figure out a "real" bitmask */
1338 sshc->orig_waitfor = data->req.keepon;
1339
1340 /* we want to use the _sending_ function even when the socket turns
1341 out readable as the underlying libssh sftp send function will deal
1342 with both accordingly */
1343 conn->cselect_bits = CURL_CSELECT_OUT;
1344
1345 /* since we don't really wait for anything at this point, we want the
1346 state machine to move on as soon as possible so we set a very short
1347 timeout here */
1348 Curl_expire(data, 0, EXPIRE_RUN_NOW);
1349
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001350 state(data, SSH_STOP);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001351 break;
1352 }
1353
1354 case SSH_SFTP_CREATE_DIRS_INIT:
1355 if(strlen(protop->path) > 1) {
1356 sshc->slash_pos = protop->path + 1; /* ignore the leading '/' */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001357 state(data, SSH_SFTP_CREATE_DIRS);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001358 }
1359 else {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001360 state(data, SSH_SFTP_UPLOAD_INIT);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001361 }
1362 break;
1363
1364 case SSH_SFTP_CREATE_DIRS:
1365 sshc->slash_pos = strchr(sshc->slash_pos, '/');
1366 if(sshc->slash_pos) {
1367 *sshc->slash_pos = 0;
1368
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001369 infof(data, "Creating directory '%s'", protop->path);
1370 state(data, SSH_SFTP_CREATE_DIRS_MKDIR);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001371 break;
1372 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001373 state(data, SSH_SFTP_UPLOAD_INIT);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001374 break;
1375
1376 case SSH_SFTP_CREATE_DIRS_MKDIR:
1377 /* 'mode' - parameter is preliminary - default to 0644 */
1378 rc = sftp_mkdir(sshc->sftp_session, protop->path,
1379 (mode_t)data->set.new_directory_perms);
1380 *sshc->slash_pos = '/';
1381 ++sshc->slash_pos;
1382 if(rc < 0) {
1383 /*
1384 * Abort if failure wasn't that the dir already exists or the
1385 * permission was denied (creation might succeed further down the
1386 * path) - retry on unspecific FAILURE also
1387 */
1388 err = sftp_get_error(sshc->sftp_session);
1389 if((err != SSH_FX_FILE_ALREADY_EXISTS) &&
1390 (err != SSH_FX_FAILURE) &&
1391 (err != SSH_FX_PERMISSION_DENIED)) {
1392 MOVE_TO_SFTP_CLOSE_STATE();
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001393 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001394 }
1395 rc = 0; /* clear rc and continue */
1396 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001397 state(data, SSH_SFTP_CREATE_DIRS);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001398 break;
1399
1400 case SSH_SFTP_READDIR_INIT:
1401 Curl_pgrsSetDownloadSize(data, -1);
1402 if(data->set.opt_no_body) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001403 state(data, SSH_STOP);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001404 break;
1405 }
1406
1407 /*
1408 * This is a directory that we are trying to get, so produce a directory
1409 * listing
1410 */
1411 sshc->sftp_dir = sftp_opendir(sshc->sftp_session,
1412 protop->path);
1413 if(!sshc->sftp_dir) {
1414 failf(data, "Could not open directory for reading: %s",
1415 ssh_get_error(sshc->ssh_session));
1416 MOVE_TO_SFTP_CLOSE_STATE();
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001417 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001418 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001419 state(data, SSH_SFTP_READDIR);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001420 break;
1421
1422 case SSH_SFTP_READDIR:
1423
1424 if(sshc->readdir_attrs)
1425 sftp_attributes_free(sshc->readdir_attrs);
1426
1427 sshc->readdir_attrs = sftp_readdir(sshc->sftp_session, sshc->sftp_dir);
1428 if(sshc->readdir_attrs) {
1429 sshc->readdir_filename = sshc->readdir_attrs->name;
1430 sshc->readdir_longentry = sshc->readdir_attrs->longname;
Elliott Hughesb1ef70f2018-10-30 11:28:38 -07001431 sshc->readdir_len = strlen(sshc->readdir_filename);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001432
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001433 if(data->set.list_only) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08001434 char *tmpLine;
1435
1436 tmpLine = aprintf("%s\n", sshc->readdir_filename);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001437 if(!tmpLine) {
1438 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001439 sshc->actualcode = CURLE_OUT_OF_MEMORY;
1440 break;
1441 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001442 result = Curl_client_write(data, CLIENTWRITE_BODY,
Elliott Hughes0128fe42018-02-27 14:57:55 -08001443 tmpLine, sshc->readdir_len + 1);
1444 free(tmpLine);
1445
1446 if(result) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001447 state(data, SSH_STOP);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001448 break;
1449 }
1450 /* since this counts what we send to the client, we include the
1451 newline in this counter */
1452 data->req.bytecount += sshc->readdir_len + 1;
1453
1454 /* output debug output if that is requested */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001455 Curl_debug(data, CURLINFO_DATA_OUT, (char *)sshc->readdir_filename,
1456 sshc->readdir_len);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001457 }
1458 else {
Elliott Hughesb1ef70f2018-10-30 11:28:38 -07001459 sshc->readdir_currLen = strlen(sshc->readdir_longentry);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001460 sshc->readdir_totalLen = 80 + sshc->readdir_currLen;
1461 sshc->readdir_line = calloc(sshc->readdir_totalLen, 1);
1462 if(!sshc->readdir_line) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001463 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001464 sshc->actualcode = CURLE_OUT_OF_MEMORY;
1465 break;
1466 }
1467
1468 memcpy(sshc->readdir_line, sshc->readdir_longentry,
1469 sshc->readdir_currLen);
1470 if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
1471 ((sshc->readdir_attrs->permissions & S_IFMT) ==
1472 S_IFLNK)) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001473 sshc->readdir_linkPath = aprintf("%s%s", protop->path,
1474 sshc->readdir_filename);
1475
1476 if(!sshc->readdir_linkPath) {
1477 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001478 sshc->actualcode = CURLE_OUT_OF_MEMORY;
1479 break;
1480 }
1481
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001482 state(data, SSH_SFTP_READDIR_LINK);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001483 break;
1484 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001485 state(data, SSH_SFTP_READDIR_BOTTOM);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001486 break;
1487 }
1488 }
Haibo Huang31944072019-11-06 02:28:57 -08001489 else if(sftp_dir_eof(sshc->sftp_dir)) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001490 state(data, SSH_SFTP_READDIR_DONE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001491 break;
1492 }
1493 else {
1494 failf(data, "Could not open remote file for reading: %s",
1495 ssh_get_error(sshc->ssh_session));
1496 MOVE_TO_SFTP_CLOSE_STATE();
1497 break;
1498 }
1499 break;
1500
1501 case SSH_SFTP_READDIR_LINK:
1502 if(sshc->readdir_link_attrs)
1503 sftp_attributes_free(sshc->readdir_link_attrs);
1504
1505 sshc->readdir_link_attrs = sftp_lstat(sshc->sftp_session,
1506 sshc->readdir_linkPath);
1507 if(sshc->readdir_link_attrs == 0) {
1508 failf(data, "Could not read symlink for reading: %s",
1509 ssh_get_error(sshc->ssh_session));
1510 MOVE_TO_SFTP_CLOSE_STATE();
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001511 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001512 }
1513
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001514 if(!sshc->readdir_link_attrs->name) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08001515 sshc->readdir_tmp = sftp_readlink(sshc->sftp_session,
1516 sshc->readdir_linkPath);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001517 if(!sshc->readdir_filename)
Elliott Hughes0128fe42018-02-27 14:57:55 -08001518 sshc->readdir_len = 0;
1519 else
Elliott Hughesb1ef70f2018-10-30 11:28:38 -07001520 sshc->readdir_len = strlen(sshc->readdir_tmp);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001521 sshc->readdir_longentry = NULL;
1522 sshc->readdir_filename = sshc->readdir_tmp;
1523 }
1524 else {
Elliott Hughesb1ef70f2018-10-30 11:28:38 -07001525 sshc->readdir_len = strlen(sshc->readdir_link_attrs->name);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001526 sshc->readdir_filename = sshc->readdir_link_attrs->name;
1527 sshc->readdir_longentry = sshc->readdir_link_attrs->longname;
1528 }
1529
1530 Curl_safefree(sshc->readdir_linkPath);
1531
1532 /* get room for the filename and extra output */
1533 sshc->readdir_totalLen += 4 + sshc->readdir_len;
1534 new_readdir_line = Curl_saferealloc(sshc->readdir_line,
1535 sshc->readdir_totalLen);
1536 if(!new_readdir_line) {
1537 sshc->readdir_line = NULL;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001538 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001539 sshc->actualcode = CURLE_OUT_OF_MEMORY;
1540 break;
1541 }
1542 sshc->readdir_line = new_readdir_line;
1543
Haibo Huang21926d52019-01-08 14:27:10 -08001544 sshc->readdir_currLen += msnprintf(sshc->readdir_line +
1545 sshc->readdir_currLen,
1546 sshc->readdir_totalLen -
1547 sshc->readdir_currLen,
1548 " -> %s",
1549 sshc->readdir_filename);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001550
1551 sftp_attributes_free(sshc->readdir_link_attrs);
1552 sshc->readdir_link_attrs = NULL;
1553 sshc->readdir_filename = NULL;
1554 sshc->readdir_longentry = NULL;
1555
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001556 state(data, SSH_SFTP_READDIR_BOTTOM);
Elliott Hughesb1ef70f2018-10-30 11:28:38 -07001557 /* FALLTHROUGH */
Elliott Hughes0128fe42018-02-27 14:57:55 -08001558 case SSH_SFTP_READDIR_BOTTOM:
Haibo Huang21926d52019-01-08 14:27:10 -08001559 sshc->readdir_currLen += msnprintf(sshc->readdir_line +
1560 sshc->readdir_currLen,
1561 sshc->readdir_totalLen -
1562 sshc->readdir_currLen, "\n");
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001563 result = Curl_client_write(data, CLIENTWRITE_BODY,
Elliott Hughes0128fe42018-02-27 14:57:55 -08001564 sshc->readdir_line,
1565 sshc->readdir_currLen);
1566
1567 if(!result) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08001568 /* output debug output if that is requested */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001569 Curl_debug(data, CURLINFO_DATA_OUT, sshc->readdir_line,
1570 sshc->readdir_currLen);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001571 data->req.bytecount += sshc->readdir_currLen;
1572 }
1573 Curl_safefree(sshc->readdir_line);
1574 ssh_string_free_char(sshc->readdir_tmp);
1575 sshc->readdir_tmp = NULL;
1576
1577 if(result) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001578 state(data, SSH_STOP);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001579 }
1580 else
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001581 state(data, SSH_SFTP_READDIR);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001582 break;
1583
1584 case SSH_SFTP_READDIR_DONE:
1585 sftp_closedir(sshc->sftp_dir);
1586 sshc->sftp_dir = NULL;
1587
1588 /* no data to transfer */
Haibo Huang65021c72019-03-27 15:37:23 -07001589 Curl_setup_transfer(data, -1, -1, FALSE, -1);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001590 state(data, SSH_STOP);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001591 break;
1592
1593 case SSH_SFTP_DOWNLOAD_INIT:
1594 /*
1595 * Work on getting the specified file
1596 */
1597 if(sshc->sftp_file)
1598 sftp_close(sshc->sftp_file);
1599
1600 sshc->sftp_file = sftp_open(sshc->sftp_session, protop->path,
1601 O_RDONLY, (mode_t)data->set.new_file_perms);
1602 if(!sshc->sftp_file) {
1603 failf(data, "Could not open remote file for reading: %s",
1604 ssh_get_error(sshc->ssh_session));
1605
1606 MOVE_TO_SFTP_CLOSE_STATE();
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001607 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001608 }
1609
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001610 state(data, SSH_SFTP_DOWNLOAD_STAT);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001611 break;
1612
1613 case SSH_SFTP_DOWNLOAD_STAT:
1614 {
1615 sftp_attributes attrs;
1616 curl_off_t size;
1617
1618 attrs = sftp_fstat(sshc->sftp_file);
1619 if(!attrs ||
1620 !(attrs->flags & SSH_FILEXFER_ATTR_SIZE) ||
1621 (attrs->size == 0)) {
1622 /*
1623 * sftp_fstat didn't return an error, so maybe the server
1624 * just doesn't support stat()
1625 * OR the server doesn't return a file size with a stat()
1626 * OR file size is 0
1627 */
1628 data->req.size = -1;
1629 data->req.maxdownload = -1;
1630 Curl_pgrsSetDownloadSize(data, -1);
1631 size = 0;
1632 }
1633 else {
1634 size = attrs->size;
1635
1636 sftp_attributes_free(attrs);
1637
1638 if(size < 0) {
1639 failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
1640 return CURLE_BAD_DOWNLOAD_RESUME;
1641 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001642 if(data->state.use_range) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08001643 curl_off_t from, to;
1644 char *ptr;
1645 char *ptr2;
1646 CURLofft to_t;
1647 CURLofft from_t;
1648
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001649 from_t = curlx_strtoofft(data->state.range, &ptr, 0, &from);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001650 if(from_t == CURL_OFFT_FLOW) {
1651 return CURLE_RANGE_ERROR;
1652 }
1653 while(*ptr && (ISSPACE(*ptr) || (*ptr == '-')))
1654 ptr++;
1655 to_t = curlx_strtoofft(ptr, &ptr2, 0, &to);
1656 if(to_t == CURL_OFFT_FLOW) {
1657 return CURLE_RANGE_ERROR;
1658 }
1659 if((to_t == CURL_OFFT_INVAL) /* no "to" value given */
1660 || (to >= size)) {
1661 to = size - 1;
1662 }
1663 if(from_t) {
1664 /* from is relative to end of file */
1665 from = size - to;
1666 to = size - 1;
1667 }
1668 if(from > size) {
1669 failf(data, "Offset (%"
1670 CURL_FORMAT_CURL_OFF_T ") was beyond file size (%"
1671 CURL_FORMAT_CURL_OFF_T ")", from, size);
1672 return CURLE_BAD_DOWNLOAD_RESUME;
1673 }
1674 if(from > to) {
1675 from = to;
1676 size = 0;
1677 }
1678 else {
1679 size = to - from + 1;
1680 }
1681
1682 rc = sftp_seek64(sshc->sftp_file, from);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001683 if(rc) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08001684 MOVE_TO_SFTP_CLOSE_STATE();
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001685 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001686 }
1687 }
1688 data->req.size = size;
1689 data->req.maxdownload = size;
1690 Curl_pgrsSetDownloadSize(data, size);
1691 }
1692
1693 /* We can resume if we can seek to the resume position */
1694 if(data->state.resume_from) {
1695 if(data->state.resume_from < 0) {
1696 /* We're supposed to download the last abs(from) bytes */
1697 if((curl_off_t)size < -data->state.resume_from) {
1698 failf(data, "Offset (%"
1699 CURL_FORMAT_CURL_OFF_T ") was beyond file size (%"
1700 CURL_FORMAT_CURL_OFF_T ")",
1701 data->state.resume_from, size);
1702 return CURLE_BAD_DOWNLOAD_RESUME;
1703 }
1704 /* download from where? */
1705 data->state.resume_from += size;
1706 }
1707 else {
1708 if((curl_off_t)size < data->state.resume_from) {
1709 failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
1710 ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
1711 data->state.resume_from, size);
1712 return CURLE_BAD_DOWNLOAD_RESUME;
1713 }
1714 }
Elliott Hughes0128fe42018-02-27 14:57:55 -08001715 /* Now store the number of bytes we are expected to download */
1716 data->req.size = size - data->state.resume_from;
1717 data->req.maxdownload = size - data->state.resume_from;
1718 Curl_pgrsSetDownloadSize(data,
1719 size - data->state.resume_from);
1720
1721 rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001722 if(rc) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08001723 MOVE_TO_SFTP_CLOSE_STATE();
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001724 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001725 }
1726 }
1727 }
1728
1729 /* Setup the actual download */
1730 if(data->req.size == 0) {
1731 /* no data to transfer */
Haibo Huang65021c72019-03-27 15:37:23 -07001732 Curl_setup_transfer(data, -1, -1, FALSE, -1);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001733 infof(data, "File already completely downloaded");
1734 state(data, SSH_STOP);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001735 break;
1736 }
Haibo Huang65021c72019-03-27 15:37:23 -07001737 Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001738
1739 /* not set by Curl_setup_transfer to preserve keepon bits */
1740 conn->writesockfd = conn->sockfd;
1741
1742 /* we want to use the _receiving_ function even when the socket turns
1743 out writableable as the underlying libssh recv function will deal
1744 with both accordingly */
1745 conn->cselect_bits = CURL_CSELECT_IN;
1746
1747 if(result) {
1748 /* this should never occur; the close state should be entered
1749 at the time the error occurs */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001750 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001751 sshc->actualcode = result;
1752 }
1753 else {
1754 sshc->sftp_recv_state = 0;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001755 state(data, SSH_STOP);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001756 }
1757 break;
1758
1759 case SSH_SFTP_CLOSE:
1760 if(sshc->sftp_file) {
1761 sftp_close(sshc->sftp_file);
1762 sshc->sftp_file = NULL;
1763 }
1764 Curl_safefree(protop->path);
1765
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001766 DEBUGF(infof(data, "SFTP DONE done"));
Elliott Hughes0128fe42018-02-27 14:57:55 -08001767
1768 /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT
1769 After nextstate is executed, the control should come back to
1770 SSH_SFTP_CLOSE to pass the correct result back */
1771 if(sshc->nextstate != SSH_NO_STATE &&
1772 sshc->nextstate != SSH_SFTP_CLOSE) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001773 state(data, sshc->nextstate);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001774 sshc->nextstate = SSH_SFTP_CLOSE;
1775 }
1776 else {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001777 state(data, SSH_STOP);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001778 result = sshc->actualcode;
1779 }
1780 break;
1781
1782 case SSH_SFTP_SHUTDOWN:
1783 /* during times we get here due to a broken transfer and then the
1784 sftp_handle might not have been taken down so make sure that is done
1785 before we proceed */
1786
1787 if(sshc->sftp_file) {
1788 sftp_close(sshc->sftp_file);
1789 sshc->sftp_file = NULL;
1790 }
1791
1792 if(sshc->sftp_session) {
1793 sftp_free(sshc->sftp_session);
1794 sshc->sftp_session = NULL;
1795 }
1796
Haibo Huang51d9d882019-02-06 01:36:06 -08001797 SSH_STRING_FREE_CHAR(sshc->homedir);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001798 data->state.most_recent_ftp_entrypath = NULL;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001799
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001800 state(data, SSH_SESSION_DISCONNECT);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001801 break;
1802
Elliott Hughes0128fe42018-02-27 14:57:55 -08001803 case SSH_SCP_TRANS_INIT:
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001804 result = Curl_getworkingpath(data, sshc->homedir, &protop->path);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001805 if(result) {
1806 sshc->actualcode = result;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001807 state(data, SSH_STOP);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001808 break;
1809 }
1810
1811 /* Functions from the SCP subsystem cannot handle/return SSH_AGAIN */
1812 ssh_set_blocking(sshc->ssh_session, 1);
1813
1814 if(data->set.upload) {
1815 if(data->state.infilesize < 0) {
1816 failf(data, "SCP requires a known file size for upload");
1817 sshc->actualcode = CURLE_UPLOAD_FAILED;
1818 MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001819 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001820 }
1821
1822 sshc->scp_session =
1823 ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, protop->path);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001824 state(data, SSH_SCP_UPLOAD_INIT);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001825 }
1826 else {
1827 sshc->scp_session =
1828 ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, protop->path);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001829 state(data, SSH_SCP_DOWNLOAD_INIT);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001830 }
1831
1832 if(!sshc->scp_session) {
1833 err_msg = ssh_get_error(sshc->ssh_session);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001834 failf(data, "%s", err_msg);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001835 MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED);
1836 }
1837
1838 break;
1839
1840 case SSH_SCP_UPLOAD_INIT:
1841
1842 rc = ssh_scp_init(sshc->scp_session);
1843 if(rc != SSH_OK) {
1844 err_msg = ssh_get_error(sshc->ssh_session);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001845 failf(data, "%s", err_msg);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001846 MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001847 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001848 }
1849
1850 rc = ssh_scp_push_file(sshc->scp_session, protop->path,
1851 data->state.infilesize,
1852 (int)data->set.new_file_perms);
1853 if(rc != SSH_OK) {
1854 err_msg = ssh_get_error(sshc->ssh_session);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001855 failf(data, "%s", err_msg);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001856 MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001857 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001858 }
1859
1860 /* upload data */
Haibo Huang65021c72019-03-27 15:37:23 -07001861 Curl_setup_transfer(data, -1, data->req.size, FALSE, FIRSTSOCKET);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001862
1863 /* not set by Curl_setup_transfer to preserve keepon bits */
1864 conn->sockfd = conn->writesockfd;
1865
1866 /* store this original bitmask setup to use later on if we can't
1867 figure out a "real" bitmask */
1868 sshc->orig_waitfor = data->req.keepon;
1869
1870 /* we want to use the _sending_ function even when the socket turns
1871 out readable as the underlying libssh scp send function will deal
1872 with both accordingly */
1873 conn->cselect_bits = CURL_CSELECT_OUT;
1874
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001875 state(data, SSH_STOP);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001876
1877 break;
1878
1879 case SSH_SCP_DOWNLOAD_INIT:
1880
1881 rc = ssh_scp_init(sshc->scp_session);
1882 if(rc != SSH_OK) {
1883 err_msg = ssh_get_error(sshc->ssh_session);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001884 failf(data, "%s", err_msg);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001885 MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001886 break;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001887 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001888 state(data, SSH_SCP_DOWNLOAD);
Elliott Hughesb1ef70f2018-10-30 11:28:38 -07001889 /* FALLTHROUGH */
Elliott Hughes0128fe42018-02-27 14:57:55 -08001890
1891 case SSH_SCP_DOWNLOAD:{
1892 curl_off_t bytecount;
1893
1894 rc = ssh_scp_pull_request(sshc->scp_session);
1895 if(rc != SSH_SCP_REQUEST_NEWFILE) {
1896 err_msg = ssh_get_error(sshc->ssh_session);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001897 failf(data, "%s", err_msg);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001898 MOVE_TO_ERROR_STATE(CURLE_REMOTE_FILE_NOT_FOUND);
1899 break;
1900 }
1901
1902 /* download data */
1903 bytecount = ssh_scp_request_get_size(sshc->scp_session);
1904 data->req.maxdownload = (curl_off_t) bytecount;
Haibo Huang65021c72019-03-27 15:37:23 -07001905 Curl_setup_transfer(data, FIRSTSOCKET, bytecount, FALSE, -1);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001906
1907 /* not set by Curl_setup_transfer to preserve keepon bits */
1908 conn->writesockfd = conn->sockfd;
1909
1910 /* we want to use the _receiving_ function even when the socket turns
1911 out writableable as the underlying libssh recv function will deal
1912 with both accordingly */
1913 conn->cselect_bits = CURL_CSELECT_IN;
1914
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001915 state(data, SSH_STOP);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001916 break;
1917 }
1918 case SSH_SCP_DONE:
1919 if(data->set.upload)
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001920 state(data, SSH_SCP_SEND_EOF);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001921 else
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001922 state(data, SSH_SCP_CHANNEL_FREE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001923 break;
1924
1925 case SSH_SCP_SEND_EOF:
1926 if(sshc->scp_session) {
1927 rc = ssh_scp_close(sshc->scp_session);
1928 if(rc == SSH_AGAIN) {
1929 /* Currently the ssh_scp_close handles waiting for EOF in
1930 * blocking way.
1931 */
1932 break;
1933 }
1934 if(rc != SSH_OK) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001935 infof(data, "Failed to close libssh scp channel: %s",
Elliott Hughes0128fe42018-02-27 14:57:55 -08001936 ssh_get_error(sshc->ssh_session));
1937 }
1938 }
1939
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001940 state(data, SSH_SCP_CHANNEL_FREE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08001941 break;
1942
1943 case SSH_SCP_CHANNEL_FREE:
1944 if(sshc->scp_session) {
1945 ssh_scp_free(sshc->scp_session);
1946 sshc->scp_session = NULL;
1947 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001948 DEBUGF(infof(data, "SCP DONE phase complete"));
Elliott Hughes0128fe42018-02-27 14:57:55 -08001949
1950 ssh_set_blocking(sshc->ssh_session, 0);
1951
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001952 state(data, SSH_SESSION_DISCONNECT);
Elliott Hughesb1ef70f2018-10-30 11:28:38 -07001953 /* FALLTHROUGH */
Elliott Hughes0128fe42018-02-27 14:57:55 -08001954
1955 case SSH_SESSION_DISCONNECT:
1956 /* during weird times when we've been prematurely aborted, the channel
1957 is still alive when we reach this state and we MUST kill the channel
1958 properly first */
1959 if(sshc->scp_session) {
1960 ssh_scp_free(sshc->scp_session);
1961 sshc->scp_session = NULL;
1962 }
1963
1964 ssh_disconnect(sshc->ssh_session);
1965
Haibo Huang51d9d882019-02-06 01:36:06 -08001966 SSH_STRING_FREE_CHAR(sshc->homedir);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001967 data->state.most_recent_ftp_entrypath = NULL;
Elliott Hughes0128fe42018-02-27 14:57:55 -08001968
Elliott Hughes34dd5f42021-08-10 13:01:18 -07001969 state(data, SSH_SESSION_FREE);
Elliott Hughesb1ef70f2018-10-30 11:28:38 -07001970 /* FALLTHROUGH */
Elliott Hughes0128fe42018-02-27 14:57:55 -08001971 case SSH_SESSION_FREE:
1972 if(sshc->ssh_session) {
1973 ssh_free(sshc->ssh_session);
1974 sshc->ssh_session = NULL;
1975 }
1976
1977 /* worst-case scenario cleanup */
1978
1979 DEBUGASSERT(sshc->ssh_session == NULL);
1980 DEBUGASSERT(sshc->scp_session == NULL);
1981
1982 if(sshc->readdir_tmp) {
1983 ssh_string_free_char(sshc->readdir_tmp);
1984 sshc->readdir_tmp = NULL;
1985 }
1986
1987 if(sshc->quote_attrs)
1988 sftp_attributes_free(sshc->quote_attrs);
1989
1990 if(sshc->readdir_attrs)
1991 sftp_attributes_free(sshc->readdir_attrs);
1992
1993 if(sshc->readdir_link_attrs)
1994 sftp_attributes_free(sshc->readdir_link_attrs);
1995
1996 if(sshc->privkey)
1997 ssh_key_free(sshc->privkey);
1998 if(sshc->pubkey)
1999 ssh_key_free(sshc->pubkey);
2000
2001 Curl_safefree(sshc->rsa_pub);
2002 Curl_safefree(sshc->rsa);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002003 Curl_safefree(sshc->quote_path1);
2004 Curl_safefree(sshc->quote_path2);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002005 Curl_safefree(sshc->readdir_line);
2006 Curl_safefree(sshc->readdir_linkPath);
Haibo Huang51d9d882019-02-06 01:36:06 -08002007 SSH_STRING_FREE_CHAR(sshc->homedir);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002008
2009 /* the code we are about to return */
2010 result = sshc->actualcode;
2011
2012 memset(sshc, 0, sizeof(struct ssh_conn));
2013
2014 connclose(conn, "SSH session free");
2015 sshc->state = SSH_SESSION_FREE; /* current */
2016 sshc->nextstate = SSH_NO_STATE;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002017 state(data, SSH_STOP);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002018 break;
2019
2020 case SSH_QUIT:
2021 /* fallthrough, just stop! */
2022 default:
2023 /* internal error */
2024 sshc->nextstate = SSH_NO_STATE;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002025 state(data, SSH_STOP);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002026 break;
2027
2028 }
2029 } while(!rc && (sshc->state != SSH_STOP));
2030
2031
2032 if(rc == SSH_AGAIN) {
2033 /* we would block, we need to wait for the socket to be ready (in the
2034 right direction too)! */
2035 *block = TRUE;
2036 }
2037
2038 return result;
2039}
2040
2041
2042/* called by the multi interface to figure out what socket(s) to wait for and
2043 for what actions in the DO_DONE, PERFORM and WAITPERFORM states */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002044static int myssh_getsock(struct Curl_easy *data,
2045 struct connectdata *conn,
2046 curl_socket_t *sock)
Elliott Hughes0128fe42018-02-27 14:57:55 -08002047{
2048 int bitmap = GETSOCK_BLANK;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002049 (void)data;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002050 sock[0] = conn->sock[FIRSTSOCKET];
2051
2052 if(conn->waitfor & KEEP_RECV)
2053 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
2054
2055 if(conn->waitfor & KEEP_SEND)
2056 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
2057
2058 return bitmap;
2059}
2060
Elliott Hughes0128fe42018-02-27 14:57:55 -08002061static void myssh_block2waitfor(struct connectdata *conn, bool block)
2062{
2063 struct ssh_conn *sshc = &conn->proto.sshc;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002064
2065 /* If it didn't block, or nothing was returned by ssh_get_poll_flags
2066 * have the original set */
2067 conn->waitfor = sshc->orig_waitfor;
2068
2069 if(block) {
Haibo Huang34ab3462019-05-22 00:50:27 -07002070 int dir = ssh_get_poll_flags(sshc->ssh_session);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002071 if(dir & SSH_READ_PENDING) {
2072 /* translate the libssh define bits into our own bit defines */
2073 conn->waitfor = KEEP_RECV;
2074 }
2075 else if(dir & SSH_WRITE_PENDING) {
2076 conn->waitfor = KEEP_SEND;
2077 }
2078 }
2079}
2080
2081/* called repeatedly until done from multi.c */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002082static CURLcode myssh_multi_statemach(struct Curl_easy *data,
Elliott Hughes0128fe42018-02-27 14:57:55 -08002083 bool *done)
2084{
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002085 struct connectdata *conn = data->conn;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002086 struct ssh_conn *sshc = &conn->proto.sshc;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002087 bool block; /* we store the status and use that to provide a ssh_getsock()
2088 implementation */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002089 CURLcode result = myssh_statemach_act(data, &block);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002090
Elliott Hughes0128fe42018-02-27 14:57:55 -08002091 *done = (sshc->state == SSH_STOP) ? TRUE : FALSE;
2092 myssh_block2waitfor(conn, block);
2093
2094 return result;
2095}
2096
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002097static CURLcode myssh_block_statemach(struct Curl_easy *data,
Elliott Hughes0128fe42018-02-27 14:57:55 -08002098 bool disconnect)
2099{
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002100 struct connectdata *conn = data->conn;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002101 struct ssh_conn *sshc = &conn->proto.sshc;
2102 CURLcode result = CURLE_OK;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002103
2104 while((sshc->state != SSH_STOP) && !result) {
2105 bool block;
2106 timediff_t left = 1000;
2107 struct curltime now = Curl_now();
2108
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002109 result = myssh_statemach_act(data, &block);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002110 if(result)
2111 break;
2112
2113 if(!disconnect) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002114 if(Curl_pgrsUpdate(data))
Elliott Hughes0128fe42018-02-27 14:57:55 -08002115 return CURLE_ABORTED_BY_CALLBACK;
2116
2117 result = Curl_speedcheck(data, now);
2118 if(result)
2119 break;
2120
2121 left = Curl_timeleft(data, NULL, FALSE);
2122 if(left < 0) {
2123 failf(data, "Operation timed out");
2124 return CURLE_OPERATION_TIMEDOUT;
2125 }
2126 }
2127
Haibo Huang31944072019-11-06 02:28:57 -08002128 if(block) {
Haibo Huang34ab3462019-05-22 00:50:27 -07002129 curl_socket_t fd_read = conn->sock[FIRSTSOCKET];
Elliott Hughes0128fe42018-02-27 14:57:55 -08002130 /* wait for the socket to become ready */
2131 (void) Curl_socket_check(fd_read, CURL_SOCKET_BAD,
2132 CURL_SOCKET_BAD, left > 1000 ? 1000 : left);
2133 }
2134
2135 }
2136
2137 return result;
2138}
2139
2140/*
2141 * SSH setup connection
2142 */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002143static CURLcode myssh_setup_connection(struct Curl_easy *data,
2144 struct connectdata *conn)
Elliott Hughes0128fe42018-02-27 14:57:55 -08002145{
2146 struct SSHPROTO *ssh;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002147 (void)conn;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002148
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002149 data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
Elliott Hughes0128fe42018-02-27 14:57:55 -08002150 if(!ssh)
2151 return CURLE_OUT_OF_MEMORY;
2152
2153 return CURLE_OK;
2154}
2155
2156static Curl_recv scp_recv, sftp_recv;
2157static Curl_send scp_send, sftp_send;
2158
2159/*
2160 * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to
2161 * do protocol-specific actions at connect-time.
2162 */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002163static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
Elliott Hughes0128fe42018-02-27 14:57:55 -08002164{
2165 struct ssh_conn *ssh;
2166 CURLcode result;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002167 struct connectdata *conn = data->conn;
Haibo Huang51d9d882019-02-06 01:36:06 -08002168 curl_socket_t sock = conn->sock[FIRSTSOCKET];
Haibo Huang24c77a12020-04-29 13:49:57 -07002169 int rc;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002170
2171 /* initialize per-handle data if not already */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002172 if(!data->req.p.ssh)
2173 myssh_setup_connection(data, conn);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002174
2175 /* We default to persistent connections. We set this already in this connect
2176 function to make the re-use checks properly be able to check this bit. */
2177 connkeep(conn, "SSH default");
2178
2179 if(conn->handler->protocol & CURLPROTO_SCP) {
2180 conn->recv[FIRSTSOCKET] = scp_recv;
2181 conn->send[FIRSTSOCKET] = scp_send;
2182 }
2183 else {
2184 conn->recv[FIRSTSOCKET] = sftp_recv;
2185 conn->send[FIRSTSOCKET] = sftp_send;
2186 }
2187
2188 ssh = &conn->proto.sshc;
2189
2190 ssh->ssh_session = ssh_new();
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002191 if(!ssh->ssh_session) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08002192 failf(data, "Failure initialising ssh session");
2193 return CURLE_FAILED_INIT;
2194 }
2195
Haibo Huang24c77a12020-04-29 13:49:57 -07002196 rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_HOST, conn->host.name);
2197 if(rc != SSH_OK) {
2198 failf(data, "Could not set remote host");
2199 return CURLE_FAILED_INIT;
2200 }
Haibo Huang51d9d882019-02-06 01:36:06 -08002201
Haibo Huang24c77a12020-04-29 13:49:57 -07002202 rc = ssh_options_parse_config(ssh->ssh_session, NULL);
2203 if(rc != SSH_OK) {
2204 infof(data, "Could not parse SSH configuration files");
2205 /* ignore */
2206 }
2207
2208 rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_FD, &sock);
2209 if(rc != SSH_OK) {
2210 failf(data, "Could not set socket");
2211 return CURLE_FAILED_INIT;
2212 }
2213
2214 if(conn->user && conn->user[0] != '\0') {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002215 infof(data, "User: %s", conn->user);
Haibo Huang24c77a12020-04-29 13:49:57 -07002216 rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_USER, conn->user);
2217 if(rc != SSH_OK) {
2218 failf(data, "Could not set user");
2219 return CURLE_FAILED_INIT;
2220 }
Elliott Hughes0128fe42018-02-27 14:57:55 -08002221 }
2222
2223 if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002224 infof(data, "Known hosts: %s", data->set.str[STRING_SSH_KNOWNHOSTS]);
Haibo Huang24c77a12020-04-29 13:49:57 -07002225 rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_KNOWNHOSTS,
2226 data->set.str[STRING_SSH_KNOWNHOSTS]);
2227 if(rc != SSH_OK) {
2228 failf(data, "Could not set known hosts file path");
2229 return CURLE_FAILED_INIT;
2230 }
Elliott Hughes0128fe42018-02-27 14:57:55 -08002231 }
2232
Haibo Huang24c77a12020-04-29 13:49:57 -07002233 if(conn->remote_port) {
2234 rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_PORT,
2235 &conn->remote_port);
2236 if(rc != SSH_OK) {
2237 failf(data, "Could not set remote port");
2238 return CURLE_FAILED_INIT;
2239 }
2240 }
Elliott Hughes0128fe42018-02-27 14:57:55 -08002241
2242 if(data->set.ssh_compression) {
Haibo Huang24c77a12020-04-29 13:49:57 -07002243 rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_COMPRESSION,
2244 "zlib,zlib@openssh.com,none");
2245 if(rc != SSH_OK) {
2246 failf(data, "Could not set compression");
2247 return CURLE_FAILED_INIT;
2248 }
Elliott Hughes0128fe42018-02-27 14:57:55 -08002249 }
2250
2251 ssh->privkey = NULL;
2252 ssh->pubkey = NULL;
2253
2254 if(data->set.str[STRING_SSH_PUBLIC_KEY]) {
Haibo Huang24c77a12020-04-29 13:49:57 -07002255 rc = ssh_pki_import_pubkey_file(data->set.str[STRING_SSH_PUBLIC_KEY],
2256 &ssh->pubkey);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002257 if(rc != SSH_OK) {
2258 failf(data, "Could not load public key file");
Haibo Huang24c77a12020-04-29 13:49:57 -07002259 return CURLE_FAILED_INIT;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002260 }
2261 }
2262
2263 /* we do not verify here, we do it at the state machine,
2264 * after connection */
2265
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002266 state(data, SSH_INIT);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002267
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002268 result = myssh_multi_statemach(data, done);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002269
2270 return result;
2271}
2272
2273/* called from multi.c while DOing */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002274static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done)
Elliott Hughes0128fe42018-02-27 14:57:55 -08002275{
2276 CURLcode result;
2277
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002278 result = myssh_multi_statemach(data, dophase_done);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002279
2280 if(*dophase_done) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002281 DEBUGF(infof(data, "DO phase is complete"));
Elliott Hughes0128fe42018-02-27 14:57:55 -08002282 }
2283 return result;
2284}
2285
2286/*
2287 ***********************************************************************
2288 *
2289 * scp_perform()
2290 *
2291 * This is the actual DO function for SCP. Get a file according to
2292 * the options previously setup.
2293 */
2294
2295static
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002296CURLcode scp_perform(struct Curl_easy *data,
Elliott Hughes0128fe42018-02-27 14:57:55 -08002297 bool *connected, bool *dophase_done)
2298{
2299 CURLcode result = CURLE_OK;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002300 struct connectdata *conn = data->conn;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002301
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002302 DEBUGF(infof(data, "DO phase starts"));
Elliott Hughes0128fe42018-02-27 14:57:55 -08002303
2304 *dophase_done = FALSE; /* not done yet */
2305
2306 /* start the first command in the DO phase */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002307 state(data, SSH_SCP_TRANS_INIT);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002308
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002309 result = myssh_multi_statemach(data, dophase_done);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002310
2311 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
2312
2313 if(*dophase_done) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002314 DEBUGF(infof(data, "DO phase is complete"));
Elliott Hughes0128fe42018-02-27 14:57:55 -08002315 }
2316
2317 return result;
2318}
2319
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002320static CURLcode myssh_do_it(struct Curl_easy *data, bool *done)
Elliott Hughes0128fe42018-02-27 14:57:55 -08002321{
2322 CURLcode result;
2323 bool connected = 0;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002324 struct connectdata *conn = data->conn;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002325 struct ssh_conn *sshc = &conn->proto.sshc;
2326
2327 *done = FALSE; /* default to false */
2328
2329 data->req.size = -1; /* make sure this is unknown at this point */
2330
2331 sshc->actualcode = CURLE_OK; /* reset error code */
2332 sshc->secondCreateDirs = 0; /* reset the create dir attempt state
2333 variable */
2334
2335 Curl_pgrsSetUploadCounter(data, 0);
2336 Curl_pgrsSetDownloadCounter(data, 0);
2337 Curl_pgrsSetUploadSize(data, -1);
2338 Curl_pgrsSetDownloadSize(data, -1);
2339
2340 if(conn->handler->protocol & CURLPROTO_SCP)
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002341 result = scp_perform(data, &connected, done);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002342 else
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002343 result = sftp_perform(data, &connected, done);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002344
2345 return result;
2346}
2347
2348/* BLOCKING, but the function is using the state machine so the only reason
2349 this is still blocking is that the multi interface code has no support for
2350 disconnecting operations that takes a while */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002351static CURLcode scp_disconnect(struct Curl_easy *data,
2352 struct connectdata *conn,
Elliott Hughes0128fe42018-02-27 14:57:55 -08002353 bool dead_connection)
2354{
2355 CURLcode result = CURLE_OK;
2356 struct ssh_conn *ssh = &conn->proto.sshc;
2357 (void) dead_connection;
2358
2359 if(ssh->ssh_session) {
2360 /* only if there's a session still around to use! */
2361
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002362 state(data, SSH_SESSION_DISCONNECT);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002363
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002364 result = myssh_block_statemach(data, TRUE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002365 }
2366
2367 return result;
2368}
2369
2370/* generic done function for both SCP and SFTP called from their specific
2371 done functions */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002372static CURLcode myssh_done(struct Curl_easy *data, CURLcode status)
Elliott Hughes0128fe42018-02-27 14:57:55 -08002373{
2374 CURLcode result = CURLE_OK;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002375 struct SSHPROTO *protop = data->req.p.ssh;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002376
2377 if(!status) {
Haibo Huang34ab3462019-05-22 00:50:27 -07002378 /* run the state-machine */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002379 result = myssh_block_statemach(data, FALSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002380 }
2381 else
2382 result = status;
2383
2384 if(protop)
2385 Curl_safefree(protop->path);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002386 if(Curl_pgrsDone(data))
Elliott Hughes0128fe42018-02-27 14:57:55 -08002387 return CURLE_ABORTED_BY_CALLBACK;
2388
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002389 data->req.keepon = 0; /* clear all bits */
Elliott Hughes0128fe42018-02-27 14:57:55 -08002390 return result;
2391}
2392
2393
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002394static CURLcode scp_done(struct Curl_easy *data, CURLcode status,
Elliott Hughes0128fe42018-02-27 14:57:55 -08002395 bool premature)
2396{
2397 (void) premature; /* not used */
2398
2399 if(!status)
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002400 state(data, SSH_SCP_DONE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002401
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002402 return myssh_done(data, status);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002403
2404}
2405
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002406static ssize_t scp_send(struct Curl_easy *data, int sockindex,
Elliott Hughes0128fe42018-02-27 14:57:55 -08002407 const void *mem, size_t len, CURLcode *err)
2408{
2409 int rc;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002410 struct connectdata *conn = data->conn;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002411 (void) sockindex; /* we only support SCP on the fixed known primary socket */
2412 (void) err;
2413
2414 rc = ssh_scp_write(conn->proto.sshc.scp_session, mem, len);
2415
2416#if 0
2417 /* The following code is misleading, mostly added as wishful thinking
2418 * that libssh at some point will implement non-blocking ssh_scp_write/read.
2419 * Currently rc can only be number of bytes read or SSH_ERROR. */
2420 myssh_block2waitfor(conn, (rc == SSH_AGAIN) ? TRUE : FALSE);
2421
2422 if(rc == SSH_AGAIN) {
2423 *err = CURLE_AGAIN;
2424 return 0;
2425 }
2426 else
2427#endif
2428 if(rc != SSH_OK) {
2429 *err = CURLE_SSH;
2430 return -1;
2431 }
2432
2433 return len;
2434}
2435
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002436static ssize_t scp_recv(struct Curl_easy *data, int sockindex,
Elliott Hughes0128fe42018-02-27 14:57:55 -08002437 char *mem, size_t len, CURLcode *err)
2438{
2439 ssize_t nread;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002440 struct connectdata *conn = data->conn;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002441 (void) err;
2442 (void) sockindex; /* we only support SCP on the fixed known primary socket */
2443
2444 /* libssh returns int */
2445 nread = ssh_scp_read(conn->proto.sshc.scp_session, mem, len);
2446
2447#if 0
2448 /* The following code is misleading, mostly added as wishful thinking
2449 * that libssh at some point will implement non-blocking ssh_scp_write/read.
2450 * Currently rc can only be SSH_OK or SSH_ERROR. */
2451
2452 myssh_block2waitfor(conn, (nread == SSH_AGAIN) ? TRUE : FALSE);
2453 if(nread == SSH_AGAIN) {
2454 *err = CURLE_AGAIN;
2455 nread = -1;
2456 }
2457#endif
2458
2459 return nread;
2460}
2461
2462/*
2463 * =============== SFTP ===============
2464 */
2465
2466/*
2467 ***********************************************************************
2468 *
2469 * sftp_perform()
2470 *
2471 * This is the actual DO function for SFTP. Get a file/directory according to
2472 * the options previously setup.
2473 */
2474
2475static
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002476CURLcode sftp_perform(struct Curl_easy *data,
Elliott Hughes0128fe42018-02-27 14:57:55 -08002477 bool *connected,
2478 bool *dophase_done)
2479{
2480 CURLcode result = CURLE_OK;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002481 struct connectdata *conn = data->conn;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002482
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002483 DEBUGF(infof(data, "DO phase starts"));
Elliott Hughes0128fe42018-02-27 14:57:55 -08002484
2485 *dophase_done = FALSE; /* not done yet */
2486
2487 /* start the first command in the DO phase */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002488 state(data, SSH_SFTP_QUOTE_INIT);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002489
2490 /* run the state-machine */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002491 result = myssh_multi_statemach(data, dophase_done);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002492
2493 *connected = conn->bits.tcpconnect[FIRSTSOCKET];
2494
2495 if(*dophase_done) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002496 DEBUGF(infof(data, "DO phase is complete"));
Elliott Hughes0128fe42018-02-27 14:57:55 -08002497 }
2498
2499 return result;
2500}
2501
2502/* called from multi.c while DOing */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002503static CURLcode sftp_doing(struct Curl_easy *data,
Elliott Hughes0128fe42018-02-27 14:57:55 -08002504 bool *dophase_done)
2505{
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002506 CURLcode result = myssh_multi_statemach(data, dophase_done);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002507 if(*dophase_done) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002508 DEBUGF(infof(data, "DO phase is complete"));
Elliott Hughes0128fe42018-02-27 14:57:55 -08002509 }
2510 return result;
2511}
2512
2513/* BLOCKING, but the function is using the state machine so the only reason
2514 this is still blocking is that the multi interface code has no support for
2515 disconnecting operations that takes a while */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002516static CURLcode sftp_disconnect(struct Curl_easy *data,
2517 struct connectdata *conn,
2518 bool dead_connection)
Elliott Hughes0128fe42018-02-27 14:57:55 -08002519{
2520 CURLcode result = CURLE_OK;
2521 (void) dead_connection;
2522
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002523 DEBUGF(infof(data, "SSH DISCONNECT starts now"));
Elliott Hughes0128fe42018-02-27 14:57:55 -08002524
2525 if(conn->proto.sshc.ssh_session) {
2526 /* only if there's a session still around to use! */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002527 state(data, SSH_SFTP_SHUTDOWN);
2528 result = myssh_block_statemach(data, TRUE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002529 }
2530
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002531 DEBUGF(infof(data, "SSH DISCONNECT is done"));
Elliott Hughes0128fe42018-02-27 14:57:55 -08002532
2533 return result;
2534
2535}
2536
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002537static CURLcode sftp_done(struct Curl_easy *data, CURLcode status,
2538 bool premature)
Elliott Hughes0128fe42018-02-27 14:57:55 -08002539{
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002540 struct connectdata *conn = data->conn;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002541 struct ssh_conn *sshc = &conn->proto.sshc;
2542
2543 if(!status) {
2544 /* Post quote commands are executed after the SFTP_CLOSE state to avoid
2545 errors that could happen due to open file handles during POSTQUOTE
2546 operation */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002547 if(!premature && data->set.postquote && !conn->bits.retry)
Elliott Hughes0128fe42018-02-27 14:57:55 -08002548 sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002549 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002550 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002551 return myssh_done(data, status);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002552}
2553
2554/* return number of sent bytes */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002555static ssize_t sftp_send(struct Curl_easy *data, int sockindex,
Elliott Hughes0128fe42018-02-27 14:57:55 -08002556 const void *mem, size_t len, CURLcode *err)
2557{
2558 ssize_t nwrite;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002559 struct connectdata *conn = data->conn;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002560 (void)sockindex;
2561
2562 nwrite = sftp_write(conn->proto.sshc.sftp_file, mem, len);
2563
2564 myssh_block2waitfor(conn, FALSE);
2565
2566#if 0 /* not returned by libssh on write */
2567 if(nwrite == SSH_AGAIN) {
2568 *err = CURLE_AGAIN;
2569 nwrite = 0;
2570 }
2571 else
2572#endif
2573 if(nwrite < 0) {
2574 *err = CURLE_SSH;
2575 nwrite = -1;
2576 }
2577
2578 return nwrite;
2579}
2580
2581/*
2582 * Return number of received (decrypted) bytes
2583 * or <0 on error
2584 */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002585static ssize_t sftp_recv(struct Curl_easy *data, int sockindex,
Elliott Hughes0128fe42018-02-27 14:57:55 -08002586 char *mem, size_t len, CURLcode *err)
2587{
2588 ssize_t nread;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002589 struct connectdata *conn = data->conn;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002590 (void)sockindex;
2591
Elliott Hughes1ef06ba2018-05-30 15:43:58 -07002592 DEBUGASSERT(len < CURL_MAX_READ_SIZE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002593
2594 switch(conn->proto.sshc.sftp_recv_state) {
2595 case 0:
2596 conn->proto.sshc.sftp_file_index =
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002597 sftp_async_read_begin(conn->proto.sshc.sftp_file,
2598 (uint32_t)len);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002599 if(conn->proto.sshc.sftp_file_index < 0) {
2600 *err = CURLE_RECV_ERROR;
2601 return -1;
2602 }
2603
Elliott Hughesb1ef70f2018-10-30 11:28:38 -07002604 /* FALLTHROUGH */
Elliott Hughes0128fe42018-02-27 14:57:55 -08002605 case 1:
2606 conn->proto.sshc.sftp_recv_state = 1;
2607
2608 nread = sftp_async_read(conn->proto.sshc.sftp_file,
2609 mem, (uint32_t)len,
2610 conn->proto.sshc.sftp_file_index);
2611
2612 myssh_block2waitfor(conn, (nread == SSH_AGAIN)?TRUE:FALSE);
2613
2614 if(nread == SSH_AGAIN) {
2615 *err = CURLE_AGAIN;
2616 return -1;
2617 }
2618 else if(nread < 0) {
2619 *err = CURLE_RECV_ERROR;
2620 return -1;
2621 }
2622
2623 conn->proto.sshc.sftp_recv_state = 0;
2624 return nread;
2625
2626 default:
2627 /* we never reach here */
2628 return -1;
2629 }
2630}
2631
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002632static void sftp_quote(struct Curl_easy *data)
Elliott Hughes0128fe42018-02-27 14:57:55 -08002633{
2634 const char *cp;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002635 struct connectdata *conn = data->conn;
2636 struct SSHPROTO *protop = data->req.p.ssh;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002637 struct ssh_conn *sshc = &conn->proto.sshc;
2638 CURLcode result;
2639
2640 /*
2641 * Support some of the "FTP" commands
2642 */
2643 char *cmd = sshc->quote_item->data;
2644 sshc->acceptfail = FALSE;
2645
2646 /* if a command starts with an asterisk, which a legal SFTP command never
2647 can, the command will be allowed to fail without it causing any
2648 aborts or cancels etc. It will cause libcurl to act as if the command
2649 is successful, whatever the server reponds. */
2650
2651 if(cmd[0] == '*') {
2652 cmd++;
2653 sshc->acceptfail = TRUE;
2654 }
2655
2656 if(strcasecompare("pwd", cmd)) {
2657 /* output debug output if that is requested */
2658 char *tmp = aprintf("257 \"%s\" is current directory.\n",
2659 protop->path);
2660 if(!tmp) {
2661 sshc->actualcode = CURLE_OUT_OF_MEMORY;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002662 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002663 sshc->nextstate = SSH_NO_STATE;
2664 return;
2665 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002666 Curl_debug(data, CURLINFO_HEADER_OUT, (char *) "PWD\n", 4);
2667 Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp));
2668
Elliott Hughes0128fe42018-02-27 14:57:55 -08002669 /* this sends an FTP-like "header" to the header callback so that the
2670 current directory can be read very similar to how it is read when
2671 using ordinary FTP. */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002672 result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
Elliott Hughes0128fe42018-02-27 14:57:55 -08002673 free(tmp);
2674 if(result) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002675 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002676 sshc->nextstate = SSH_NO_STATE;
2677 sshc->actualcode = result;
2678 }
2679 else
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002680 state(data, SSH_SFTP_NEXT_QUOTE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002681 return;
2682 }
2683
2684 /*
2685 * the arguments following the command must be separated from the
2686 * command with a space so we can check for it unconditionally
2687 */
2688 cp = strchr(cmd, ' ');
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002689 if(!cp) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08002690 failf(data, "Syntax error in SFTP command. Supply parameter(s)!");
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002691 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002692 sshc->nextstate = SSH_NO_STATE;
2693 sshc->actualcode = CURLE_QUOTE_ERROR;
2694 return;
2695 }
2696
2697 /*
2698 * also, every command takes at least one argument so we get that
2699 * first argument right now
2700 */
2701 result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir);
2702 if(result) {
2703 if(result == CURLE_OUT_OF_MEMORY)
2704 failf(data, "Out of memory");
2705 else
2706 failf(data, "Syntax error: Bad first parameter");
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002707 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002708 sshc->nextstate = SSH_NO_STATE;
2709 sshc->actualcode = result;
2710 return;
2711 }
2712
2713 /*
2714 * SFTP is a binary protocol, so we don't send text commands
2715 * to the server. Instead, we scan for commands used by
2716 * OpenSSH's sftp program and call the appropriate libssh
2717 * functions.
2718 */
2719 if(strncasecompare(cmd, "chgrp ", 6) ||
2720 strncasecompare(cmd, "chmod ", 6) ||
Haibo Huangb5a52b92020-10-28 22:18:23 -07002721 strncasecompare(cmd, "chown ", 6) ||
2722 strncasecompare(cmd, "atime ", 6) ||
2723 strncasecompare(cmd, "mtime ", 6)) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08002724 /* attribute change */
2725
2726 /* sshc->quote_path1 contains the mode to set */
2727 /* get the destination */
2728 result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
2729 if(result) {
2730 if(result == CURLE_OUT_OF_MEMORY)
2731 failf(data, "Out of memory");
2732 else
Haibo Huangb5a52b92020-10-28 22:18:23 -07002733 failf(data, "Syntax error in chgrp/chmod/chown/atime/mtime: "
Elliott Hughes0128fe42018-02-27 14:57:55 -08002734 "Bad second parameter");
2735 Curl_safefree(sshc->quote_path1);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002736 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002737 sshc->nextstate = SSH_NO_STATE;
2738 sshc->actualcode = result;
2739 return;
2740 }
2741 sshc->quote_attrs = NULL;
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002742 state(data, SSH_SFTP_QUOTE_STAT);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002743 return;
2744 }
2745 if(strncasecompare(cmd, "ln ", 3) ||
2746 strncasecompare(cmd, "symlink ", 8)) {
2747 /* symbolic linking */
2748 /* sshc->quote_path1 is the source */
2749 /* get the destination */
2750 result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
2751 if(result) {
2752 if(result == CURLE_OUT_OF_MEMORY)
2753 failf(data, "Out of memory");
2754 else
2755 failf(data, "Syntax error in ln/symlink: Bad second parameter");
2756 Curl_safefree(sshc->quote_path1);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002757 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002758 sshc->nextstate = SSH_NO_STATE;
2759 sshc->actualcode = result;
2760 return;
2761 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002762 state(data, SSH_SFTP_QUOTE_SYMLINK);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002763 return;
2764 }
2765 else if(strncasecompare(cmd, "mkdir ", 6)) {
2766 /* create dir */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002767 state(data, SSH_SFTP_QUOTE_MKDIR);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002768 return;
2769 }
2770 else if(strncasecompare(cmd, "rename ", 7)) {
2771 /* rename file */
2772 /* first param is the source path */
2773 /* second param is the dest. path */
2774 result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
2775 if(result) {
2776 if(result == CURLE_OUT_OF_MEMORY)
2777 failf(data, "Out of memory");
2778 else
2779 failf(data, "Syntax error in rename: Bad second parameter");
2780 Curl_safefree(sshc->quote_path1);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002781 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002782 sshc->nextstate = SSH_NO_STATE;
2783 sshc->actualcode = result;
2784 return;
2785 }
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002786 state(data, SSH_SFTP_QUOTE_RENAME);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002787 return;
2788 }
2789 else if(strncasecompare(cmd, "rmdir ", 6)) {
2790 /* delete dir */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002791 state(data, SSH_SFTP_QUOTE_RMDIR);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002792 return;
2793 }
2794 else if(strncasecompare(cmd, "rm ", 3)) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002795 state(data, SSH_SFTP_QUOTE_UNLINK);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002796 return;
2797 }
2798#ifdef HAS_STATVFS_SUPPORT
2799 else if(strncasecompare(cmd, "statvfs ", 8)) {
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002800 state(data, SSH_SFTP_QUOTE_STATVFS);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002801 return;
2802 }
2803#endif
2804
2805 failf(data, "Unknown SFTP command");
2806 Curl_safefree(sshc->quote_path1);
2807 Curl_safefree(sshc->quote_path2);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002808 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002809 sshc->nextstate = SSH_NO_STATE;
2810 sshc->actualcode = CURLE_QUOTE_ERROR;
2811}
2812
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002813static void sftp_quote_stat(struct Curl_easy *data)
Elliott Hughes0128fe42018-02-27 14:57:55 -08002814{
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002815 struct connectdata *conn = data->conn;
Elliott Hughes0128fe42018-02-27 14:57:55 -08002816 struct ssh_conn *sshc = &conn->proto.sshc;
2817 char *cmd = sshc->quote_item->data;
2818 sshc->acceptfail = FALSE;
2819
2820 /* if a command starts with an asterisk, which a legal SFTP command never
2821 can, the command will be allowed to fail without it causing any
2822 aborts or cancels etc. It will cause libcurl to act as if the command
2823 is successful, whatever the server reponds. */
2824
2825 if(cmd[0] == '*') {
2826 cmd++;
2827 sshc->acceptfail = TRUE;
2828 }
2829
2830 /* We read the file attributes, store them in sshc->quote_attrs
2831 * and modify them accordingly to command. Then we switch to
2832 * QUOTE_SETSTAT state to write new ones.
2833 */
2834
2835 if(sshc->quote_attrs)
2836 sftp_attributes_free(sshc->quote_attrs);
2837 sshc->quote_attrs = sftp_stat(sshc->sftp_session, sshc->quote_path2);
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002838 if(!sshc->quote_attrs) {
Elliott Hughes0128fe42018-02-27 14:57:55 -08002839 Curl_safefree(sshc->quote_path1);
2840 Curl_safefree(sshc->quote_path2);
2841 failf(data, "Attempt to get SFTP stats failed: %d",
2842 sftp_get_error(sshc->sftp_session));
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002843 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002844 sshc->nextstate = SSH_NO_STATE;
2845 sshc->actualcode = CURLE_QUOTE_ERROR;
2846 return;
2847 }
2848
2849 /* Now set the new attributes... */
2850 if(strncasecompare(cmd, "chgrp", 5)) {
2851 sshc->quote_attrs->gid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10);
2852 if(sshc->quote_attrs->gid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
2853 !sshc->acceptfail) {
2854 Curl_safefree(sshc->quote_path1);
2855 Curl_safefree(sshc->quote_path2);
2856 failf(data, "Syntax error: chgrp gid not a number");
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002857 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002858 sshc->nextstate = SSH_NO_STATE;
2859 sshc->actualcode = CURLE_QUOTE_ERROR;
2860 return;
2861 }
2862 sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID;
2863 }
2864 else if(strncasecompare(cmd, "chmod", 5)) {
2865 mode_t perms;
2866 perms = (mode_t)strtoul(sshc->quote_path1, NULL, 8);
2867 /* permissions are octal */
2868 if(perms == 0 && !ISDIGIT(sshc->quote_path1[0])) {
2869 Curl_safefree(sshc->quote_path1);
2870 Curl_safefree(sshc->quote_path2);
2871 failf(data, "Syntax error: chmod permissions not a number");
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002872 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002873 sshc->nextstate = SSH_NO_STATE;
2874 sshc->actualcode = CURLE_QUOTE_ERROR;
2875 return;
2876 }
2877 sshc->quote_attrs->permissions = perms;
2878 sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
2879 }
2880 else if(strncasecompare(cmd, "chown", 5)) {
2881 sshc->quote_attrs->uid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10);
2882 if(sshc->quote_attrs->uid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
2883 !sshc->acceptfail) {
2884 Curl_safefree(sshc->quote_path1);
2885 Curl_safefree(sshc->quote_path2);
2886 failf(data, "Syntax error: chown uid not a number");
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002887 state(data, SSH_SFTP_CLOSE);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002888 sshc->nextstate = SSH_NO_STATE;
2889 sshc->actualcode = CURLE_QUOTE_ERROR;
2890 return;
2891 }
2892 sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID;
2893 }
Haibo Huangb5a52b92020-10-28 22:18:23 -07002894 else if(strncasecompare(cmd, "atime", 5)) {
2895 time_t date = Curl_getdate_capped(sshc->quote_path1);
2896 if(date == -1) {
2897 Curl_safefree(sshc->quote_path1);
2898 Curl_safefree(sshc->quote_path2);
2899 failf(data, "Syntax error: incorrect access date format");
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002900 state(data, SSH_SFTP_CLOSE);
Haibo Huangb5a52b92020-10-28 22:18:23 -07002901 sshc->nextstate = SSH_NO_STATE;
2902 sshc->actualcode = CURLE_QUOTE_ERROR;
2903 return;
2904 }
2905 sshc->quote_attrs->atime = (uint32_t)date;
2906 sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_ACMODTIME;
2907 }
2908 else if(strncasecompare(cmd, "mtime", 5)) {
2909 time_t date = Curl_getdate_capped(sshc->quote_path1);
2910 if(date == -1) {
2911 Curl_safefree(sshc->quote_path1);
2912 Curl_safefree(sshc->quote_path2);
2913 failf(data, "Syntax error: incorrect modification date format");
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002914 state(data, SSH_SFTP_CLOSE);
Haibo Huangb5a52b92020-10-28 22:18:23 -07002915 sshc->nextstate = SSH_NO_STATE;
2916 sshc->actualcode = CURLE_QUOTE_ERROR;
2917 return;
2918 }
2919 sshc->quote_attrs->mtime = (uint32_t)date;
2920 sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_ACMODTIME;
2921 }
Elliott Hughes0128fe42018-02-27 14:57:55 -08002922
2923 /* Now send the completed structure... */
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002924 state(data, SSH_SFTP_QUOTE_SETSTAT);
Elliott Hughes0128fe42018-02-27 14:57:55 -08002925 return;
2926}
2927
Haibo Huang445085a2019-09-11 13:33:50 -07002928CURLcode Curl_ssh_init(void)
2929{
2930 if(ssh_init()) {
2931 DEBUGF(fprintf(stderr, "Error: libssh_init failed\n"));
2932 return CURLE_FAILED_INIT;
2933 }
2934 return CURLE_OK;
2935}
2936
2937void Curl_ssh_cleanup(void)
2938{
2939 (void)ssh_finalize();
2940}
2941
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002942void Curl_ssh_version(char *buffer, size_t buflen)
Haibo Huang445085a2019-09-11 13:33:50 -07002943{
Elliott Hughes34dd5f42021-08-10 13:01:18 -07002944 (void)msnprintf(buffer, buflen, "libssh/%s", CURL_LIBSSH_VERSION);
Haibo Huang445085a2019-09-11 13:33:50 -07002945}
Elliott Hughes0128fe42018-02-27 14:57:55 -08002946
2947#endif /* USE_LIBSSH */