VirtualBox

source: vbox/trunk/src/libs/curl-8.7.1/lib/vssh/libssh.c@ 107412

Last change on this file since 107412 was 104083, checked in by vboxsync, 14 months ago

curl-8.7.1: Applied and adjusted our curl changes to 8.4.0. bugref:10639

  • Property svn:eol-style set to native
File size: 85.8 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Red Hat, Inc.
9 *
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
15 * are also available at https://curl.se/docs/copyright.html.
16 *
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 * SPDX-License-Identifier: curl
25 *
26 ***************************************************************************/
27
28#include "curl_setup.h"
29
30#ifdef USE_LIBSSH
31
32#include <limits.h>
33
34/* in 0.10.0 or later, ignore deprecated warnings */
35#define SSH_SUPPRESS_DEPRECATED
36#include <libssh/libssh.h>
37#include <libssh/sftp.h>
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_NETDB_H
46#include <netdb.h>
47#endif
48#ifdef __VMS
49#include <in.h>
50#include <inet.h>
51#endif
52
53#include <curl/curl.h>
54#include "urldata.h"
55#include "sendf.h"
56#include "hostip.h"
57#include "progress.h"
58#include "transfer.h"
59#include "escape.h"
60#include "http.h" /* for HTTP proxy tunnel stuff */
61#include "ssh.h"
62#include "url.h"
63#include "speedcheck.h"
64#include "getinfo.h"
65#include "strdup.h"
66#include "strcase.h"
67#include "vtls/vtls.h"
68#include "cfilters.h"
69#include "connect.h"
70#include "inet_ntop.h"
71#include "parsedate.h" /* for the week day and month names */
72#include "sockaddr.h" /* required for Curl_sockaddr_storage */
73#include "strtoofft.h"
74#include "multiif.h"
75#include "select.h"
76#include "warnless.h"
77#include "curl_path.h"
78
79#ifdef HAVE_SYS_STAT_H
80#include <sys/stat.h>
81#endif
82#ifdef HAVE_UNISTD_H
83#include <unistd.h>
84#endif
85#ifdef HAVE_FCNTL_H
86#include <fcntl.h>
87#endif
88
89/* The last 3 #include files should be in this order */
90#include "curl_printf.h"
91#include "curl_memory.h"
92#include "memdebug.h"
93
94/* A recent macro provided by libssh. Or make our own. */
95#ifndef SSH_STRING_FREE_CHAR
96#define SSH_STRING_FREE_CHAR(x) \
97 do { \
98 if(x) { \
99 ssh_string_free_char(x); \
100 x = NULL; \
101 } \
102 } while(0)
103#endif
104
105/* These stat values may not be the same as the user's S_IFMT / S_IFLNK */
106#ifndef SSH_S_IFMT
107#define SSH_S_IFMT 00170000
108#endif
109#ifndef SSH_S_IFLNK
110#define SSH_S_IFLNK 0120000
111#endif
112
113/* Local functions: */
114static CURLcode myssh_connect(struct Curl_easy *data, bool *done);
115static CURLcode myssh_multi_statemach(struct Curl_easy *data,
116 bool *done);
117static CURLcode myssh_do_it(struct Curl_easy *data, bool *done);
118
119static CURLcode scp_done(struct Curl_easy *data,
120 CURLcode, bool premature);
121static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done);
122static CURLcode scp_disconnect(struct Curl_easy *data,
123 struct connectdata *conn,
124 bool dead_connection);
125
126static CURLcode sftp_done(struct Curl_easy *data,
127 CURLcode, bool premature);
128static CURLcode sftp_doing(struct Curl_easy *data,
129 bool *dophase_done);
130static CURLcode sftp_disconnect(struct Curl_easy *data,
131 struct connectdata *conn,
132 bool dead);
133static
134CURLcode sftp_perform(struct Curl_easy *data,
135 bool *connected,
136 bool *dophase_done);
137
138static void sftp_quote(struct Curl_easy *data);
139static void sftp_quote_stat(struct Curl_easy *data);
140static int myssh_getsock(struct Curl_easy *data,
141 struct connectdata *conn, curl_socket_t *sock);
142
143static CURLcode myssh_setup_connection(struct Curl_easy *data,
144 struct connectdata *conn);
145
146/*
147 * SCP protocol handler.
148 */
149
150const struct Curl_handler Curl_handler_scp = {
151 "SCP", /* scheme */
152 myssh_setup_connection, /* setup_connection */
153 myssh_do_it, /* do_it */
154 scp_done, /* done */
155 ZERO_NULL, /* do_more */
156 myssh_connect, /* connect_it */
157 myssh_multi_statemach, /* connecting */
158 scp_doing, /* doing */
159 myssh_getsock, /* proto_getsock */
160 myssh_getsock, /* doing_getsock */
161 ZERO_NULL, /* domore_getsock */
162 myssh_getsock, /* perform_getsock */
163 scp_disconnect, /* disconnect */
164 ZERO_NULL, /* write_resp */
165 ZERO_NULL, /* connection_check */
166 ZERO_NULL, /* attach connection */
167 PORT_SSH, /* defport */
168 CURLPROTO_SCP, /* protocol */
169 CURLPROTO_SCP, /* family */
170 PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
171};
172
173/*
174 * SFTP protocol handler.
175 */
176
177const struct Curl_handler Curl_handler_sftp = {
178 "SFTP", /* scheme */
179 myssh_setup_connection, /* setup_connection */
180 myssh_do_it, /* do_it */
181 sftp_done, /* done */
182 ZERO_NULL, /* do_more */
183 myssh_connect, /* connect_it */
184 myssh_multi_statemach, /* connecting */
185 sftp_doing, /* doing */
186 myssh_getsock, /* proto_getsock */
187 myssh_getsock, /* doing_getsock */
188 ZERO_NULL, /* domore_getsock */
189 myssh_getsock, /* perform_getsock */
190 sftp_disconnect, /* disconnect */
191 ZERO_NULL, /* write_resp */
192 ZERO_NULL, /* connection_check */
193 ZERO_NULL, /* attach connection */
194 PORT_SSH, /* defport */
195 CURLPROTO_SFTP, /* protocol */
196 CURLPROTO_SFTP, /* family */
197 PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
198 | PROTOPT_NOURLQUERY /* flags */
199};
200
201static CURLcode sftp_error_to_CURLE(int err)
202{
203 switch(err) {
204 case SSH_FX_OK:
205 return CURLE_OK;
206
207 case SSH_FX_NO_SUCH_FILE:
208 case SSH_FX_NO_SUCH_PATH:
209 return CURLE_REMOTE_FILE_NOT_FOUND;
210
211 case SSH_FX_PERMISSION_DENIED:
212 case SSH_FX_WRITE_PROTECT:
213 return CURLE_REMOTE_ACCESS_DENIED;
214
215 case SSH_FX_FILE_ALREADY_EXISTS:
216 return CURLE_REMOTE_FILE_EXISTS;
217
218 default:
219 break;
220 }
221
222 return CURLE_SSH;
223}
224
225#ifndef DEBUGBUILD
226#define state(x,y) mystate(x,y)
227#else
228#define state(x,y) mystate(x,y, __LINE__)
229#endif
230
231/*
232 * SSH State machine related code
233 */
234/* This is the ONLY way to change SSH state! */
235static void mystate(struct Curl_easy *data, sshstate nowstate
236#ifdef DEBUGBUILD
237 , int lineno
238#endif
239 )
240{
241 struct connectdata *conn = data->conn;
242 struct ssh_conn *sshc = &conn->proto.sshc;
243#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
244 /* for debug purposes */
245 static const char *const names[] = {
246 "SSH_STOP",
247 "SSH_INIT",
248 "SSH_S_STARTUP",
249 "SSH_HOSTKEY",
250 "SSH_AUTHLIST",
251 "SSH_AUTH_PKEY_INIT",
252 "SSH_AUTH_PKEY",
253 "SSH_AUTH_PASS_INIT",
254 "SSH_AUTH_PASS",
255 "SSH_AUTH_AGENT_INIT",
256 "SSH_AUTH_AGENT_LIST",
257 "SSH_AUTH_AGENT",
258 "SSH_AUTH_HOST_INIT",
259 "SSH_AUTH_HOST",
260 "SSH_AUTH_KEY_INIT",
261 "SSH_AUTH_KEY",
262 "SSH_AUTH_GSSAPI",
263 "SSH_AUTH_DONE",
264 "SSH_SFTP_INIT",
265 "SSH_SFTP_REALPATH",
266 "SSH_SFTP_QUOTE_INIT",
267 "SSH_SFTP_POSTQUOTE_INIT",
268 "SSH_SFTP_QUOTE",
269 "SSH_SFTP_NEXT_QUOTE",
270 "SSH_SFTP_QUOTE_STAT",
271 "SSH_SFTP_QUOTE_SETSTAT",
272 "SSH_SFTP_QUOTE_SYMLINK",
273 "SSH_SFTP_QUOTE_MKDIR",
274 "SSH_SFTP_QUOTE_RENAME",
275 "SSH_SFTP_QUOTE_RMDIR",
276 "SSH_SFTP_QUOTE_UNLINK",
277 "SSH_SFTP_QUOTE_STATVFS",
278 "SSH_SFTP_GETINFO",
279 "SSH_SFTP_FILETIME",
280 "SSH_SFTP_TRANS_INIT",
281 "SSH_SFTP_UPLOAD_INIT",
282 "SSH_SFTP_CREATE_DIRS_INIT",
283 "SSH_SFTP_CREATE_DIRS",
284 "SSH_SFTP_CREATE_DIRS_MKDIR",
285 "SSH_SFTP_READDIR_INIT",
286 "SSH_SFTP_READDIR",
287 "SSH_SFTP_READDIR_LINK",
288 "SSH_SFTP_READDIR_BOTTOM",
289 "SSH_SFTP_READDIR_DONE",
290 "SSH_SFTP_DOWNLOAD_INIT",
291 "SSH_SFTP_DOWNLOAD_STAT",
292 "SSH_SFTP_CLOSE",
293 "SSH_SFTP_SHUTDOWN",
294 "SSH_SCP_TRANS_INIT",
295 "SSH_SCP_UPLOAD_INIT",
296 "SSH_SCP_DOWNLOAD_INIT",
297 "SSH_SCP_DOWNLOAD",
298 "SSH_SCP_DONE",
299 "SSH_SCP_SEND_EOF",
300 "SSH_SCP_WAIT_EOF",
301 "SSH_SCP_WAIT_CLOSE",
302 "SSH_SCP_CHANNEL_FREE",
303 "SSH_SESSION_DISCONNECT",
304 "SSH_SESSION_FREE",
305 "QUIT"
306 };
307
308
309 if(sshc->state != nowstate) {
310 infof(data, "SSH %p state change from %s to %s (line %d)",
311 (void *) sshc, names[sshc->state], names[nowstate],
312 lineno);
313 }
314#endif
315
316 sshc->state = nowstate;
317}
318
319/* Multiple options:
320 * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5
321 * hash (90s style auth, not sure we should have it here)
322 * 2. data->set.ssh_keyfunc callback is set. Then we do trust on first
323 * use. We even save on knownhosts if CURLKHSTAT_FINE_ADD_TO_FILE
324 * is returned by it.
325 * 3. none of the above. We only accept if it is present on known hosts.
326 *
327 * Returns SSH_OK or SSH_ERROR.
328 */
329static int myssh_is_known(struct Curl_easy *data)
330{
331 int rc;
332 struct connectdata *conn = data->conn;
333 struct ssh_conn *sshc = &conn->proto.sshc;
334 ssh_key pubkey;
335 size_t hlen;
336 unsigned char *hash = NULL;
337 char *found_base64 = NULL;
338 char *known_base64 = NULL;
339 int vstate;
340 enum curl_khmatch keymatch;
341 struct curl_khkey foundkey;
342 struct curl_khkey *knownkeyp = NULL;
343 curl_sshkeycallback func =
344 data->set.ssh_keyfunc;
345
346#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
347 struct ssh_knownhosts_entry *knownhostsentry = NULL;
348 struct curl_khkey knownkey;
349#endif
350
351#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0)
352 rc = ssh_get_server_publickey(sshc->ssh_session, &pubkey);
353#else
354 rc = ssh_get_publickey(sshc->ssh_session, &pubkey);
355#endif
356 if(rc != SSH_OK)
357 return rc;
358
359 if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) {
360 int i;
361 char md5buffer[33];
362 const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5];
363
364 rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5,
365 &hash, &hlen);
366 if(rc != SSH_OK || hlen != 16) {
367 failf(data,
368 "Denied establishing ssh session: md5 fingerprint not available");
369 goto cleanup;
370 }
371
372 for(i = 0; i < 16; i++)
373 msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char)hash[i]);
374
375 infof(data, "SSH MD5 fingerprint: %s", md5buffer);
376
377 if(!strcasecompare(md5buffer, pubkey_md5)) {
378 failf(data,
379 "Denied establishing ssh session: mismatch md5 fingerprint. "
380 "Remote %s is not equal to %s", md5buffer, pubkey_md5);
381 rc = SSH_ERROR;
382 goto cleanup;
383 }
384
385 rc = SSH_OK;
386 goto cleanup;
387 }
388
389 if(data->set.ssl.primary.verifyhost != TRUE) {
390 rc = SSH_OK;
391 goto cleanup;
392 }
393
394#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
395 /* Get the known_key from the known hosts file */
396 vstate = ssh_session_get_known_hosts_entry(sshc->ssh_session,
397 &knownhostsentry);
398
399 /* Case an entry was found in a known hosts file */
400 if(knownhostsentry) {
401 if(knownhostsentry->publickey) {
402 rc = ssh_pki_export_pubkey_base64(knownhostsentry->publickey,
403 &known_base64);
404 if(rc != SSH_OK) {
405 goto cleanup;
406 }
407 knownkey.key = known_base64;
408 knownkey.len = strlen(known_base64);
409
410 switch(ssh_key_type(knownhostsentry->publickey)) {
411 case SSH_KEYTYPE_RSA:
412 knownkey.keytype = CURLKHTYPE_RSA;
413 break;
414 case SSH_KEYTYPE_RSA1:
415 knownkey.keytype = CURLKHTYPE_RSA1;
416 break;
417 case SSH_KEYTYPE_ECDSA:
418 case SSH_KEYTYPE_ECDSA_P256:
419 case SSH_KEYTYPE_ECDSA_P384:
420 case SSH_KEYTYPE_ECDSA_P521:
421 knownkey.keytype = CURLKHTYPE_ECDSA;
422 break;
423 case SSH_KEYTYPE_ED25519:
424 knownkey.keytype = CURLKHTYPE_ED25519;
425 break;
426 case SSH_KEYTYPE_DSS:
427 knownkey.keytype = CURLKHTYPE_DSS;
428 break;
429 default:
430 rc = SSH_ERROR;
431 goto cleanup;
432 }
433 knownkeyp = &knownkey;
434 }
435 }
436
437 switch(vstate) {
438 case SSH_KNOWN_HOSTS_OK:
439 keymatch = CURLKHMATCH_OK;
440 break;
441 case SSH_KNOWN_HOSTS_OTHER:
442 case SSH_KNOWN_HOSTS_NOT_FOUND:
443 case SSH_KNOWN_HOSTS_UNKNOWN:
444 case SSH_KNOWN_HOSTS_ERROR:
445 keymatch = CURLKHMATCH_MISSING;
446 break;
447 default:
448 keymatch = CURLKHMATCH_MISMATCH;
449 break;
450 }
451
452#else
453 vstate = ssh_is_server_known(sshc->ssh_session);
454 switch(vstate) {
455 case SSH_SERVER_KNOWN_OK:
456 keymatch = CURLKHMATCH_OK;
457 break;
458 case SSH_SERVER_FILE_NOT_FOUND:
459 case SSH_SERVER_NOT_KNOWN:
460 keymatch = CURLKHMATCH_MISSING;
461 break;
462 default:
463 keymatch = CURLKHMATCH_MISMATCH;
464 break;
465 }
466#endif
467
468 if(func) { /* use callback to determine action */
469 rc = ssh_pki_export_pubkey_base64(pubkey, &found_base64);
470 if(rc != SSH_OK)
471 goto cleanup;
472
473 foundkey.key = found_base64;
474 foundkey.len = strlen(found_base64);
475
476 switch(ssh_key_type(pubkey)) {
477 case SSH_KEYTYPE_RSA:
478 foundkey.keytype = CURLKHTYPE_RSA;
479 break;
480 case SSH_KEYTYPE_RSA1:
481 foundkey.keytype = CURLKHTYPE_RSA1;
482 break;
483 case SSH_KEYTYPE_ECDSA:
484#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
485 case SSH_KEYTYPE_ECDSA_P256:
486 case SSH_KEYTYPE_ECDSA_P384:
487 case SSH_KEYTYPE_ECDSA_P521:
488#endif
489 foundkey.keytype = CURLKHTYPE_ECDSA;
490 break;
491#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,7,0)
492 case SSH_KEYTYPE_ED25519:
493 foundkey.keytype = CURLKHTYPE_ED25519;
494 break;
495#endif
496 case SSH_KEYTYPE_DSS:
497 foundkey.keytype = CURLKHTYPE_DSS;
498 break;
499 default:
500 rc = SSH_ERROR;
501 goto cleanup;
502 }
503
504 Curl_set_in_callback(data, true);
505 rc = func(data, knownkeyp, /* from the knownhosts file */
506 &foundkey, /* from the remote host */
507 keymatch, data->set.ssh_keyfunc_userp);
508 Curl_set_in_callback(data, false);
509
510 switch(rc) {
511 case CURLKHSTAT_FINE_ADD_TO_FILE:
512#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0)
513 rc = ssh_session_update_known_hosts(sshc->ssh_session);
514#else
515 rc = ssh_write_knownhost(sshc->ssh_session);
516#endif
517 if(rc != SSH_OK) {
518 goto cleanup;
519 }
520 break;
521 case CURLKHSTAT_FINE:
522 break;
523 default: /* REJECT/DEFER */
524 rc = SSH_ERROR;
525 goto cleanup;
526 }
527 }
528 else {
529 if(keymatch != CURLKHMATCH_OK) {
530 rc = SSH_ERROR;
531 goto cleanup;
532 }
533 }
534 rc = SSH_OK;
535
536cleanup:
537 if(found_base64) {
538 (free)(found_base64);
539 }
540 if(known_base64) {
541 (free)(known_base64);
542 }
543 if(hash)
544 ssh_clean_pubkey_hash(&hash);
545 ssh_key_free(pubkey);
546#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
547 if(knownhostsentry) {
548 ssh_knownhosts_entry_free(knownhostsentry);
549 }
550#endif
551 return rc;
552}
553
554#define MOVE_TO_ERROR_STATE(_r) do { \
555 state(data, SSH_SESSION_DISCONNECT); \
556 sshc->actualcode = _r; \
557 rc = SSH_ERROR; \
558 } while(0)
559
560#define MOVE_TO_SFTP_CLOSE_STATE() do { \
561 state(data, SSH_SFTP_CLOSE); \
562 sshc->actualcode = \
563 sftp_error_to_CURLE(sftp_get_error(sshc->sftp_session)); \
564 rc = SSH_ERROR; \
565 } while(0)
566
567#define MOVE_TO_PASSWD_AUTH do { \
568 if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { \
569 rc = SSH_OK; \
570 state(data, SSH_AUTH_PASS_INIT); \
571 } \
572 else { \
573 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); \
574 } \
575 } while(0)
576
577#define MOVE_TO_KEY_AUTH do { \
578 if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { \
579 rc = SSH_OK; \
580 state(data, SSH_AUTH_KEY_INIT); \
581 } \
582 else { \
583 MOVE_TO_PASSWD_AUTH; \
584 } \
585 } while(0)
586
587#define MOVE_TO_GSSAPI_AUTH do { \
588 if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { \
589 rc = SSH_OK; \
590 state(data, SSH_AUTH_GSSAPI); \
591 } \
592 else { \
593 MOVE_TO_KEY_AUTH; \
594 } \
595 } while(0)
596
597static
598int myssh_auth_interactive(struct connectdata *conn)
599{
600 int rc;
601 struct ssh_conn *sshc = &conn->proto.sshc;
602 int nprompts;
603
604restart:
605 switch(sshc->kbd_state) {
606 case 0:
607 rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL);
608 if(rc == SSH_AUTH_AGAIN)
609 return SSH_AGAIN;
610
611 if(rc != SSH_AUTH_INFO)
612 return SSH_ERROR;
613
614 nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session);
615 if(nprompts != 1)
616 return SSH_ERROR;
617
618 rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, conn->passwd);
619 if(rc < 0)
620 return SSH_ERROR;
621
622 FALLTHROUGH();
623 case 1:
624 sshc->kbd_state = 1;
625
626 rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL);
627 if(rc == SSH_AUTH_AGAIN)
628 return SSH_AGAIN;
629 else if(rc == SSH_AUTH_SUCCESS)
630 rc = SSH_OK;
631 else if(rc == SSH_AUTH_INFO) {
632 nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session);
633 if(nprompts)
634 return SSH_ERROR;
635
636 sshc->kbd_state = 2;
637 goto restart;
638 }
639 else
640 rc = SSH_ERROR;
641 break;
642 case 2:
643 sshc->kbd_state = 2;
644
645 rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL);
646 if(rc == SSH_AUTH_AGAIN)
647 return SSH_AGAIN;
648 else if(rc == SSH_AUTH_SUCCESS)
649 rc = SSH_OK;
650 else
651 rc = SSH_ERROR;
652
653 break;
654 default:
655 return SSH_ERROR;
656 }
657
658 sshc->kbd_state = 0;
659 return rc;
660}
661
662/*
663 * ssh_statemach_act() runs the SSH state machine as far as it can without
664 * blocking and without reaching the end. The data the pointer 'block' points
665 * to will be set to TRUE if the libssh function returns SSH_AGAIN
666 * meaning it wants to be called again when the socket is ready
667 */
668static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
669{
670 CURLcode result = CURLE_OK;
671 struct connectdata *conn = data->conn;
672 struct SSHPROTO *protop = data->req.p.ssh;
673 struct ssh_conn *sshc = &conn->proto.sshc;
674 curl_socket_t sock = conn->sock[FIRSTSOCKET];
675 int rc = SSH_NO_ERROR, err;
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
696 state(data, SSH_S_STARTUP);
697 FALLTHROUGH();
698
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);
707 break;
708 }
709
710 state(data, SSH_HOSTKEY);
711
712 FALLTHROUGH();
713 case SSH_HOSTKEY:
714
715 rc = myssh_is_known(data);
716 if(rc != SSH_OK) {
717 MOVE_TO_ERROR_STATE(CURLE_PEER_FAILED_VERIFICATION);
718 break;
719 }
720
721 state(data, SSH_AUTHLIST);
722 FALLTHROUGH();
723 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;
734 infof(data, "Authenticated with none");
735 state(data, SSH_AUTH_DONE);
736 break;
737 }
738 else if(rc == SSH_AUTH_ERROR) {
739 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
740 break;
741 }
742
743 sshc->auth_methods = ssh_userauth_list(sshc->ssh_session, NULL);
744 if(sshc->auth_methods)
745 infof(data, "SSH authentication methods available: %s%s%s%s",
746 sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY ?
747 "public key, ": "",
748 sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC ?
749 "GSSAPI, " : "",
750 sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE ?
751 "keyboard-interactive, " : "",
752 sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD ?
753 "password": "");
754 if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) {
755 state(data, SSH_AUTH_PKEY_INIT);
756 infof(data, "Authentication using SSH public key file");
757 }
758 else if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) {
759 state(data, SSH_AUTH_GSSAPI);
760 }
761 else if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) {
762 state(data, SSH_AUTH_KEY_INIT);
763 }
764 else if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) {
765 state(data, SSH_AUTH_PASS_INIT);
766 }
767 else { /* unsupported authentication method */
768 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
769 break;
770 }
771
772 break;
773 }
774 case SSH_AUTH_PKEY_INIT:
775 if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY)) {
776 MOVE_TO_GSSAPI_AUTH;
777 break;
778 }
779
780 /* Two choices, (1) private key was given on CMD,
781 * (2) use the "default" keys. */
782 if(data->set.str[STRING_SSH_PRIVATE_KEY]) {
783 if(sshc->pubkey && !data->set.ssl.key_passwd) {
784 rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL,
785 sshc->pubkey);
786 if(rc == SSH_AUTH_AGAIN) {
787 rc = SSH_AGAIN;
788 break;
789 }
790
791 if(rc != SSH_OK) {
792 MOVE_TO_GSSAPI_AUTH;
793 break;
794 }
795 }
796
797 rc = ssh_pki_import_privkey_file(data->
798 set.str[STRING_SSH_PRIVATE_KEY],
799 data->set.ssl.key_passwd, NULL,
800 NULL, &sshc->privkey);
801 if(rc != SSH_OK) {
802 failf(data, "Could not load private key file %s",
803 data->set.str[STRING_SSH_PRIVATE_KEY]);
804 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
805 break;
806 }
807
808 state(data, SSH_AUTH_PKEY);
809 break;
810
811 }
812 else {
813 rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL,
814 data->set.ssl.key_passwd);
815 if(rc == SSH_AUTH_AGAIN) {
816 rc = SSH_AGAIN;
817 break;
818 }
819 if(rc == SSH_AUTH_SUCCESS) {
820 rc = SSH_OK;
821 sshc->authed = TRUE;
822 infof(data, "Completed public key authentication");
823 state(data, SSH_AUTH_DONE);
824 break;
825 }
826
827 MOVE_TO_GSSAPI_AUTH;
828 }
829 break;
830 case SSH_AUTH_PKEY:
831 rc = ssh_userauth_publickey(sshc->ssh_session, NULL, sshc->privkey);
832 if(rc == SSH_AUTH_AGAIN) {
833 rc = SSH_AGAIN;
834 break;
835 }
836
837 if(rc == SSH_AUTH_SUCCESS) {
838 sshc->authed = TRUE;
839 infof(data, "Completed public key authentication");
840 state(data, SSH_AUTH_DONE);
841 break;
842 }
843 else {
844 infof(data, "Failed public key authentication (rc: %d)", rc);
845 MOVE_TO_GSSAPI_AUTH;
846 }
847 break;
848
849 case SSH_AUTH_GSSAPI:
850 if(!(data->set.ssh_auth_types & CURLSSH_AUTH_GSSAPI)) {
851 MOVE_TO_KEY_AUTH;
852 break;
853 }
854
855 rc = ssh_userauth_gssapi(sshc->ssh_session);
856 if(rc == SSH_AUTH_AGAIN) {
857 rc = SSH_AGAIN;
858 break;
859 }
860
861 if(rc == SSH_AUTH_SUCCESS) {
862 rc = SSH_OK;
863 sshc->authed = TRUE;
864 infof(data, "Completed gssapi authentication");
865 state(data, SSH_AUTH_DONE);
866 break;
867 }
868
869 MOVE_TO_KEY_AUTH;
870 break;
871
872 case SSH_AUTH_KEY_INIT:
873 if(data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) {
874 state(data, SSH_AUTH_KEY);
875 }
876 else {
877 MOVE_TO_PASSWD_AUTH;
878 }
879 break;
880
881 case SSH_AUTH_KEY:
882 /* keyboard-interactive authentication */
883 rc = myssh_auth_interactive(conn);
884 if(rc == SSH_AGAIN) {
885 break;
886 }
887 if(rc == SSH_OK) {
888 sshc->authed = TRUE;
889 infof(data, "completed keyboard interactive authentication");
890 state(data, SSH_AUTH_DONE);
891 }
892 else {
893 MOVE_TO_PASSWD_AUTH;
894 }
895 break;
896
897 case SSH_AUTH_PASS_INIT:
898 if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD)) {
899 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
900 break;
901 }
902 state(data, SSH_AUTH_PASS);
903 FALLTHROUGH();
904
905 case SSH_AUTH_PASS:
906 rc = ssh_userauth_password(sshc->ssh_session, NULL, conn->passwd);
907 if(rc == SSH_AUTH_AGAIN) {
908 rc = SSH_AGAIN;
909 break;
910 }
911
912 if(rc == SSH_AUTH_SUCCESS) {
913 sshc->authed = TRUE;
914 infof(data, "Completed password authentication");
915 state(data, SSH_AUTH_DONE);
916 }
917 else {
918 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
919 }
920 break;
921
922 case SSH_AUTH_DONE:
923 if(!sshc->authed) {
924 failf(data, "Authentication failure");
925 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
926 break;
927 }
928
929 /*
930 * At this point we have an authenticated ssh session.
931 */
932 infof(data, "Authentication complete");
933
934 Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */
935
936 conn->sockfd = sock;
937 conn->writesockfd = CURL_SOCKET_BAD;
938
939 if(conn->handler->protocol == CURLPROTO_SFTP) {
940 state(data, SSH_SFTP_INIT);
941 break;
942 }
943 infof(data, "SSH CONNECT phase done");
944 state(data, SSH_STOP);
945 break;
946
947 case SSH_SFTP_INIT:
948 ssh_set_blocking(sshc->ssh_session, 1);
949
950 sshc->sftp_session = sftp_new(sshc->ssh_session);
951 if(!sshc->sftp_session) {
952 failf(data, "Failure initializing sftp session: %s",
953 ssh_get_error(sshc->ssh_session));
954 MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
955 break;
956 }
957
958 rc = sftp_init(sshc->sftp_session);
959 if(rc != SSH_OK) {
960 failf(data, "Failure initializing sftp session: %s",
961 ssh_get_error(sshc->ssh_session));
962 MOVE_TO_ERROR_STATE(sftp_error_to_CURLE(SSH_FX_FAILURE));
963 break;
964 }
965 state(data, SSH_SFTP_REALPATH);
966 FALLTHROUGH();
967 case SSH_SFTP_REALPATH:
968 /*
969 * Get the "home" directory
970 */
971 sshc->homedir = sftp_canonicalize_path(sshc->sftp_session, ".");
972 if(!sshc->homedir) {
973 MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
974 break;
975 }
976 data->state.most_recent_ftp_entrypath = sshc->homedir;
977
978 /* This is the last step in the SFTP connect phase. Do note that while
979 we get the homedir here, we get the "workingpath" in the DO action
980 since the homedir will remain the same between request but the
981 working path will not. */
982 DEBUGF(infof(data, "SSH CONNECT phase done"));
983 state(data, SSH_STOP);
984 break;
985
986 case SSH_SFTP_QUOTE_INIT:
987 result = Curl_getworkingpath(data, sshc->homedir, &protop->path);
988 if(result) {
989 sshc->actualcode = result;
990 state(data, SSH_STOP);
991 break;
992 }
993
994 if(data->set.quote) {
995 infof(data, "Sending quote commands");
996 sshc->quote_item = data->set.quote;
997 state(data, SSH_SFTP_QUOTE);
998 }
999 else {
1000 state(data, SSH_SFTP_GETINFO);
1001 }
1002 break;
1003
1004 case SSH_SFTP_POSTQUOTE_INIT:
1005 if(data->set.postquote) {
1006 infof(data, "Sending quote commands");
1007 sshc->quote_item = data->set.postquote;
1008 state(data, SSH_SFTP_QUOTE);
1009 }
1010 else {
1011 state(data, SSH_STOP);
1012 }
1013 break;
1014
1015 case SSH_SFTP_QUOTE:
1016 /* Send any quote commands */
1017 sftp_quote(data);
1018 break;
1019
1020 case SSH_SFTP_NEXT_QUOTE:
1021 Curl_safefree(sshc->quote_path1);
1022 Curl_safefree(sshc->quote_path2);
1023
1024 sshc->quote_item = sshc->quote_item->next;
1025
1026 if(sshc->quote_item) {
1027 state(data, SSH_SFTP_QUOTE);
1028 }
1029 else {
1030 if(sshc->nextstate != SSH_NO_STATE) {
1031 state(data, sshc->nextstate);
1032 sshc->nextstate = SSH_NO_STATE;
1033 }
1034 else {
1035 state(data, SSH_SFTP_GETINFO);
1036 }
1037 }
1038 break;
1039
1040 case SSH_SFTP_QUOTE_STAT:
1041 sftp_quote_stat(data);
1042 break;
1043
1044 case SSH_SFTP_QUOTE_SETSTAT:
1045 rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2,
1046 sshc->quote_attrs);
1047 if(rc && !sshc->acceptfail) {
1048 Curl_safefree(sshc->quote_path1);
1049 Curl_safefree(sshc->quote_path2);
1050 failf(data, "Attempt to set SFTP stats failed: %s",
1051 ssh_get_error(sshc->ssh_session));
1052 state(data, SSH_SFTP_CLOSE);
1053 sshc->nextstate = SSH_NO_STATE;
1054 sshc->actualcode = CURLE_QUOTE_ERROR;
1055 /* sshc->actualcode = sftp_error_to_CURLE(err);
1056 * we do not send the actual error; we return
1057 * the error the libssh2 backend is returning */
1058 break;
1059 }
1060 state(data, SSH_SFTP_NEXT_QUOTE);
1061 break;
1062
1063 case SSH_SFTP_QUOTE_SYMLINK:
1064 rc = sftp_symlink(sshc->sftp_session, sshc->quote_path2,
1065 sshc->quote_path1);
1066 if(rc && !sshc->acceptfail) {
1067 Curl_safefree(sshc->quote_path1);
1068 Curl_safefree(sshc->quote_path2);
1069 failf(data, "symlink command failed: %s",
1070 ssh_get_error(sshc->ssh_session));
1071 state(data, SSH_SFTP_CLOSE);
1072 sshc->nextstate = SSH_NO_STATE;
1073 sshc->actualcode = CURLE_QUOTE_ERROR;
1074 break;
1075 }
1076 state(data, SSH_SFTP_NEXT_QUOTE);
1077 break;
1078
1079 case SSH_SFTP_QUOTE_MKDIR:
1080 rc = sftp_mkdir(sshc->sftp_session, sshc->quote_path1,
1081 (mode_t)data->set.new_directory_perms);
1082 if(rc && !sshc->acceptfail) {
1083 Curl_safefree(sshc->quote_path1);
1084 failf(data, "mkdir command failed: %s",
1085 ssh_get_error(sshc->ssh_session));
1086 state(data, SSH_SFTP_CLOSE);
1087 sshc->nextstate = SSH_NO_STATE;
1088 sshc->actualcode = CURLE_QUOTE_ERROR;
1089 break;
1090 }
1091 state(data, SSH_SFTP_NEXT_QUOTE);
1092 break;
1093
1094 case SSH_SFTP_QUOTE_RENAME:
1095 rc = sftp_rename(sshc->sftp_session, sshc->quote_path1,
1096 sshc->quote_path2);
1097 if(rc && !sshc->acceptfail) {
1098 Curl_safefree(sshc->quote_path1);
1099 Curl_safefree(sshc->quote_path2);
1100 failf(data, "rename command failed: %s",
1101 ssh_get_error(sshc->ssh_session));
1102 state(data, SSH_SFTP_CLOSE);
1103 sshc->nextstate = SSH_NO_STATE;
1104 sshc->actualcode = CURLE_QUOTE_ERROR;
1105 break;
1106 }
1107 state(data, SSH_SFTP_NEXT_QUOTE);
1108 break;
1109
1110 case SSH_SFTP_QUOTE_RMDIR:
1111 rc = sftp_rmdir(sshc->sftp_session, sshc->quote_path1);
1112 if(rc && !sshc->acceptfail) {
1113 Curl_safefree(sshc->quote_path1);
1114 failf(data, "rmdir command failed: %s",
1115 ssh_get_error(sshc->ssh_session));
1116 state(data, SSH_SFTP_CLOSE);
1117 sshc->nextstate = SSH_NO_STATE;
1118 sshc->actualcode = CURLE_QUOTE_ERROR;
1119 break;
1120 }
1121 state(data, SSH_SFTP_NEXT_QUOTE);
1122 break;
1123
1124 case SSH_SFTP_QUOTE_UNLINK:
1125 rc = sftp_unlink(sshc->sftp_session, sshc->quote_path1);
1126 if(rc && !sshc->acceptfail) {
1127 Curl_safefree(sshc->quote_path1);
1128 failf(data, "rm command failed: %s",
1129 ssh_get_error(sshc->ssh_session));
1130 state(data, SSH_SFTP_CLOSE);
1131 sshc->nextstate = SSH_NO_STATE;
1132 sshc->actualcode = CURLE_QUOTE_ERROR;
1133 break;
1134 }
1135 state(data, SSH_SFTP_NEXT_QUOTE);
1136 break;
1137
1138 case SSH_SFTP_QUOTE_STATVFS:
1139 {
1140 sftp_statvfs_t statvfs;
1141
1142 statvfs = sftp_statvfs(sshc->sftp_session, sshc->quote_path1);
1143 if(!statvfs && !sshc->acceptfail) {
1144 Curl_safefree(sshc->quote_path1);
1145 failf(data, "statvfs command failed: %s",
1146 ssh_get_error(sshc->ssh_session));
1147 state(data, SSH_SFTP_CLOSE);
1148 sshc->nextstate = SSH_NO_STATE;
1149 sshc->actualcode = CURLE_QUOTE_ERROR;
1150 break;
1151 }
1152 else if(statvfs) {
1153 #ifdef _MSC_VER
1154 #define CURL_LIBSSH_VFS_SIZE_MASK "I64u"
1155 #else
1156 #define CURL_LIBSSH_VFS_SIZE_MASK PRIu64
1157 #endif
1158 char *tmp = aprintf("statvfs:\n"
1159 "f_bsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
1160 "f_frsize: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
1161 "f_blocks: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
1162 "f_bfree: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
1163 "f_bavail: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
1164 "f_files: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
1165 "f_ffree: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
1166 "f_favail: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
1167 "f_fsid: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
1168 "f_flag: %" CURL_LIBSSH_VFS_SIZE_MASK "\n"
1169 "f_namemax: %" CURL_LIBSSH_VFS_SIZE_MASK "\n",
1170 statvfs->f_bsize, statvfs->f_frsize,
1171 statvfs->f_blocks, statvfs->f_bfree,
1172 statvfs->f_bavail, statvfs->f_files,
1173 statvfs->f_ffree, statvfs->f_favail,
1174 statvfs->f_fsid, statvfs->f_flag,
1175 statvfs->f_namemax);
1176 sftp_statvfs_free(statvfs);
1177
1178 if(!tmp) {
1179 result = CURLE_OUT_OF_MEMORY;
1180 state(data, SSH_SFTP_CLOSE);
1181 sshc->nextstate = SSH_NO_STATE;
1182 break;
1183 }
1184
1185 result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
1186 free(tmp);
1187 if(result) {
1188 state(data, SSH_SFTP_CLOSE);
1189 sshc->nextstate = SSH_NO_STATE;
1190 sshc->actualcode = result;
1191 }
1192 }
1193 state(data, SSH_SFTP_NEXT_QUOTE);
1194 break;
1195 }
1196
1197 case SSH_SFTP_GETINFO:
1198 if(data->set.get_filetime) {
1199 state(data, SSH_SFTP_FILETIME);
1200 }
1201 else {
1202 state(data, SSH_SFTP_TRANS_INIT);
1203 }
1204 break;
1205
1206 case SSH_SFTP_FILETIME:
1207 {
1208 sftp_attributes attrs;
1209
1210 attrs = sftp_stat(sshc->sftp_session, protop->path);
1211 if(attrs) {
1212 data->info.filetime = attrs->mtime;
1213 sftp_attributes_free(attrs);
1214 }
1215
1216 state(data, SSH_SFTP_TRANS_INIT);
1217 break;
1218 }
1219
1220 case SSH_SFTP_TRANS_INIT:
1221 if(data->state.upload)
1222 state(data, SSH_SFTP_UPLOAD_INIT);
1223 else {
1224 if(protop->path[strlen(protop->path)-1] == '/')
1225 state(data, SSH_SFTP_READDIR_INIT);
1226 else
1227 state(data, SSH_SFTP_DOWNLOAD_INIT);
1228 }
1229 break;
1230
1231 case SSH_SFTP_UPLOAD_INIT:
1232 {
1233 int flags;
1234
1235 if(data->state.resume_from) {
1236 sftp_attributes attrs;
1237
1238 if(data->state.resume_from < 0) {
1239 attrs = sftp_stat(sshc->sftp_session, protop->path);
1240 if(attrs) {
1241 curl_off_t size = attrs->size;
1242 if(size < 0) {
1243 failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
1244 MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
1245 break;
1246 }
1247 data->state.resume_from = attrs->size;
1248
1249 sftp_attributes_free(attrs);
1250 }
1251 else {
1252 data->state.resume_from = 0;
1253 }
1254 }
1255 }
1256
1257 if(data->set.remote_append)
1258 /* Try to open for append, but create if nonexisting */
1259 flags = O_WRONLY|O_CREAT|O_APPEND;
1260 else if(data->state.resume_from > 0)
1261 /* If we have restart position then open for append */
1262 flags = O_WRONLY|O_APPEND;
1263 else
1264 /* Clear file before writing (normal behavior) */
1265 flags = O_WRONLY|O_CREAT|O_TRUNC;
1266
1267 if(sshc->sftp_file)
1268 sftp_close(sshc->sftp_file);
1269 sshc->sftp_file =
1270 sftp_open(sshc->sftp_session, protop->path,
1271 flags, (mode_t)data->set.new_file_perms);
1272 if(!sshc->sftp_file) {
1273 err = sftp_get_error(sshc->sftp_session);
1274
1275 if(((err == SSH_FX_NO_SUCH_FILE || err == SSH_FX_FAILURE ||
1276 err == SSH_FX_NO_SUCH_PATH)) &&
1277 (data->set.ftp_create_missing_dirs &&
1278 (strlen(protop->path) > 1))) {
1279 /* try to create the path remotely */
1280 rc = 0;
1281 sshc->secondCreateDirs = 1;
1282 state(data, SSH_SFTP_CREATE_DIRS_INIT);
1283 break;
1284 }
1285 else {
1286 MOVE_TO_SFTP_CLOSE_STATE();
1287 break;
1288 }
1289 }
1290
1291 /* If we have a restart point then we need to seek to the correct
1292 position. */
1293 if(data->state.resume_from > 0) {
1294 /* Let's read off the proper amount of bytes from the input. */
1295 if(data->set.seek_func) {
1296 Curl_set_in_callback(data, true);
1297 seekerr = data->set.seek_func(data->set.seek_client,
1298 data->state.resume_from, SEEK_SET);
1299 Curl_set_in_callback(data, false);
1300 }
1301
1302 if(seekerr != CURL_SEEKFUNC_OK) {
1303 curl_off_t passed = 0;
1304
1305 if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
1306 failf(data, "Could not seek stream");
1307 return CURLE_FTP_COULDNT_USE_REST;
1308 }
1309 /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
1310 do {
1311 char scratch[4*1024];
1312 size_t readthisamountnow =
1313 (data->state.resume_from - passed >
1314 (curl_off_t)sizeof(scratch)) ?
1315 sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed);
1316
1317 size_t actuallyread =
1318 data->state.fread_func(scratch, 1,
1319 readthisamountnow, data->state.in);
1320
1321 passed += actuallyread;
1322 if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
1323 /* this checks for greater-than only to make sure that the
1324 CURL_READFUNC_ABORT return code still aborts */
1325 failf(data, "Failed to read data");
1326 MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST);
1327 break;
1328 }
1329 } while(passed < data->state.resume_from);
1330 if(rc)
1331 break;
1332 }
1333
1334 /* now, decrease the size of the read */
1335 if(data->state.infilesize > 0) {
1336 data->state.infilesize -= data->state.resume_from;
1337 data->req.size = data->state.infilesize;
1338 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1339 }
1340
1341 rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
1342 if(rc) {
1343 MOVE_TO_SFTP_CLOSE_STATE();
1344 break;
1345 }
1346 }
1347 if(data->state.infilesize > 0) {
1348 data->req.size = data->state.infilesize;
1349 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1350 }
1351 /* upload data */
1352 Curl_xfer_setup(data, -1, -1, FALSE, FIRSTSOCKET);
1353
1354 /* not set by Curl_xfer_setup to preserve keepon bits */
1355 conn->sockfd = conn->writesockfd;
1356
1357 /* store this original bitmask setup to use later on if we can't
1358 figure out a "real" bitmask */
1359 sshc->orig_waitfor = data->req.keepon;
1360
1361 /* we want to use the _sending_ function even when the socket turns
1362 out readable as the underlying libssh sftp send function will deal
1363 with both accordingly */
1364 data->state.select_bits = CURL_CSELECT_OUT;
1365
1366 /* since we don't really wait for anything at this point, we want the
1367 state machine to move on as soon as possible so we set a very short
1368 timeout here */
1369 Curl_expire(data, 0, EXPIRE_RUN_NOW);
1370
1371 state(data, SSH_STOP);
1372 break;
1373 }
1374
1375 case SSH_SFTP_CREATE_DIRS_INIT:
1376 if(strlen(protop->path) > 1) {
1377 sshc->slash_pos = protop->path + 1; /* ignore the leading '/' */
1378 state(data, SSH_SFTP_CREATE_DIRS);
1379 }
1380 else {
1381 state(data, SSH_SFTP_UPLOAD_INIT);
1382 }
1383 break;
1384
1385 case SSH_SFTP_CREATE_DIRS:
1386 sshc->slash_pos = strchr(sshc->slash_pos, '/');
1387 if(sshc->slash_pos) {
1388 *sshc->slash_pos = 0;
1389
1390 infof(data, "Creating directory '%s'", protop->path);
1391 state(data, SSH_SFTP_CREATE_DIRS_MKDIR);
1392 break;
1393 }
1394 state(data, SSH_SFTP_UPLOAD_INIT);
1395 break;
1396
1397 case SSH_SFTP_CREATE_DIRS_MKDIR:
1398 /* 'mode' - parameter is preliminary - default to 0644 */
1399 rc = sftp_mkdir(sshc->sftp_session, protop->path,
1400 (mode_t)data->set.new_directory_perms);
1401 *sshc->slash_pos = '/';
1402 ++sshc->slash_pos;
1403 if(rc < 0) {
1404 /*
1405 * Abort if failure wasn't that the dir already exists or the
1406 * permission was denied (creation might succeed further down the
1407 * path) - retry on unspecific FAILURE also
1408 */
1409 err = sftp_get_error(sshc->sftp_session);
1410 if((err != SSH_FX_FILE_ALREADY_EXISTS) &&
1411 (err != SSH_FX_FAILURE) &&
1412 (err != SSH_FX_PERMISSION_DENIED)) {
1413 MOVE_TO_SFTP_CLOSE_STATE();
1414 break;
1415 }
1416 rc = 0; /* clear rc and continue */
1417 }
1418 state(data, SSH_SFTP_CREATE_DIRS);
1419 break;
1420
1421 case SSH_SFTP_READDIR_INIT:
1422 Curl_pgrsSetDownloadSize(data, -1);
1423 if(data->req.no_body) {
1424 state(data, SSH_STOP);
1425 break;
1426 }
1427
1428 /*
1429 * This is a directory that we are trying to get, so produce a directory
1430 * listing
1431 */
1432 sshc->sftp_dir = sftp_opendir(sshc->sftp_session,
1433 protop->path);
1434 if(!sshc->sftp_dir) {
1435 failf(data, "Could not open directory for reading: %s",
1436 ssh_get_error(sshc->ssh_session));
1437 MOVE_TO_SFTP_CLOSE_STATE();
1438 break;
1439 }
1440 state(data, SSH_SFTP_READDIR);
1441 break;
1442
1443 case SSH_SFTP_READDIR:
1444 Curl_dyn_reset(&sshc->readdir_buf);
1445 if(sshc->readdir_attrs)
1446 sftp_attributes_free(sshc->readdir_attrs);
1447
1448 sshc->readdir_attrs = sftp_readdir(sshc->sftp_session, sshc->sftp_dir);
1449 if(sshc->readdir_attrs) {
1450 sshc->readdir_filename = sshc->readdir_attrs->name;
1451 sshc->readdir_longentry = sshc->readdir_attrs->longname;
1452 sshc->readdir_len = strlen(sshc->readdir_filename);
1453
1454 if(data->set.list_only) {
1455 char *tmpLine;
1456
1457 tmpLine = aprintf("%s\n", sshc->readdir_filename);
1458 if(!tmpLine) {
1459 state(data, SSH_SFTP_CLOSE);
1460 sshc->actualcode = CURLE_OUT_OF_MEMORY;
1461 break;
1462 }
1463 result = Curl_client_write(data, CLIENTWRITE_BODY,
1464 tmpLine, sshc->readdir_len + 1);
1465 free(tmpLine);
1466
1467 if(result) {
1468 state(data, SSH_STOP);
1469 break;
1470 }
1471
1472 }
1473 else {
1474 if(Curl_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) {
1475 sshc->actualcode = CURLE_OUT_OF_MEMORY;
1476 state(data, SSH_STOP);
1477 break;
1478 }
1479
1480 if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
1481 ((sshc->readdir_attrs->permissions & SSH_S_IFMT) ==
1482 SSH_S_IFLNK)) {
1483 sshc->readdir_linkPath = aprintf("%s%s", protop->path,
1484 sshc->readdir_filename);
1485
1486 if(!sshc->readdir_linkPath) {
1487 state(data, SSH_SFTP_CLOSE);
1488 sshc->actualcode = CURLE_OUT_OF_MEMORY;
1489 break;
1490 }
1491
1492 state(data, SSH_SFTP_READDIR_LINK);
1493 break;
1494 }
1495 state(data, SSH_SFTP_READDIR_BOTTOM);
1496 break;
1497 }
1498 }
1499 else if(sftp_dir_eof(sshc->sftp_dir)) {
1500 state(data, SSH_SFTP_READDIR_DONE);
1501 break;
1502 }
1503 else {
1504 failf(data, "Could not open remote file for reading: %s",
1505 ssh_get_error(sshc->ssh_session));
1506 MOVE_TO_SFTP_CLOSE_STATE();
1507 break;
1508 }
1509 break;
1510
1511 case SSH_SFTP_READDIR_LINK:
1512 if(sshc->readdir_link_attrs)
1513 sftp_attributes_free(sshc->readdir_link_attrs);
1514
1515 sshc->readdir_link_attrs = sftp_lstat(sshc->sftp_session,
1516 sshc->readdir_linkPath);
1517 if(sshc->readdir_link_attrs == 0) {
1518 failf(data, "Could not read symlink for reading: %s",
1519 ssh_get_error(sshc->ssh_session));
1520 MOVE_TO_SFTP_CLOSE_STATE();
1521 break;
1522 }
1523
1524 if(!sshc->readdir_link_attrs->name) {
1525 sshc->readdir_tmp = sftp_readlink(sshc->sftp_session,
1526 sshc->readdir_linkPath);
1527 if(!sshc->readdir_filename)
1528 sshc->readdir_len = 0;
1529 else
1530 sshc->readdir_len = strlen(sshc->readdir_tmp);
1531 sshc->readdir_longentry = NULL;
1532 sshc->readdir_filename = sshc->readdir_tmp;
1533 }
1534 else {
1535 sshc->readdir_len = strlen(sshc->readdir_link_attrs->name);
1536 sshc->readdir_filename = sshc->readdir_link_attrs->name;
1537 sshc->readdir_longentry = sshc->readdir_link_attrs->longname;
1538 }
1539
1540 Curl_safefree(sshc->readdir_linkPath);
1541
1542 if(Curl_dyn_addf(&sshc->readdir_buf, " -> %s",
1543 sshc->readdir_filename)) {
1544 sshc->actualcode = CURLE_OUT_OF_MEMORY;
1545 break;
1546 }
1547
1548 sftp_attributes_free(sshc->readdir_link_attrs);
1549 sshc->readdir_link_attrs = NULL;
1550 sshc->readdir_filename = NULL;
1551 sshc->readdir_longentry = NULL;
1552
1553 state(data, SSH_SFTP_READDIR_BOTTOM);
1554 FALLTHROUGH();
1555 case SSH_SFTP_READDIR_BOTTOM:
1556 if(Curl_dyn_addn(&sshc->readdir_buf, "\n", 1))
1557 result = CURLE_OUT_OF_MEMORY;
1558 else
1559 result = Curl_client_write(data, CLIENTWRITE_BODY,
1560 Curl_dyn_ptr(&sshc->readdir_buf),
1561 Curl_dyn_len(&sshc->readdir_buf));
1562
1563 ssh_string_free_char(sshc->readdir_tmp);
1564 sshc->readdir_tmp = NULL;
1565
1566 if(result) {
1567 state(data, SSH_STOP);
1568 }
1569 else
1570 state(data, SSH_SFTP_READDIR);
1571 break;
1572
1573 case SSH_SFTP_READDIR_DONE:
1574 sftp_closedir(sshc->sftp_dir);
1575 sshc->sftp_dir = NULL;
1576
1577 /* no data to transfer */
1578 Curl_xfer_setup(data, -1, -1, FALSE, -1);
1579 state(data, SSH_STOP);
1580 break;
1581
1582 case SSH_SFTP_DOWNLOAD_INIT:
1583 /*
1584 * Work on getting the specified file
1585 */
1586 if(sshc->sftp_file)
1587 sftp_close(sshc->sftp_file);
1588
1589 sshc->sftp_file = sftp_open(sshc->sftp_session, protop->path,
1590 O_RDONLY, (mode_t)data->set.new_file_perms);
1591 if(!sshc->sftp_file) {
1592 failf(data, "Could not open remote file for reading: %s",
1593 ssh_get_error(sshc->ssh_session));
1594
1595 MOVE_TO_SFTP_CLOSE_STATE();
1596 break;
1597 }
1598 sftp_file_set_nonblocking(sshc->sftp_file);
1599 state(data, SSH_SFTP_DOWNLOAD_STAT);
1600 break;
1601
1602 case SSH_SFTP_DOWNLOAD_STAT:
1603 {
1604 sftp_attributes attrs;
1605 curl_off_t size;
1606
1607 attrs = sftp_fstat(sshc->sftp_file);
1608 if(!attrs ||
1609 !(attrs->flags & SSH_FILEXFER_ATTR_SIZE) ||
1610 (attrs->size == 0)) {
1611 /*
1612 * sftp_fstat didn't return an error, so maybe the server
1613 * just doesn't support stat()
1614 * OR the server doesn't return a file size with a stat()
1615 * OR file size is 0
1616 */
1617 data->req.size = -1;
1618 data->req.maxdownload = -1;
1619 Curl_pgrsSetDownloadSize(data, -1);
1620 size = 0;
1621 }
1622 else {
1623 size = attrs->size;
1624
1625 sftp_attributes_free(attrs);
1626
1627 if(size < 0) {
1628 failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
1629 return CURLE_BAD_DOWNLOAD_RESUME;
1630 }
1631 if(data->state.use_range) {
1632 curl_off_t from, to;
1633 char *ptr;
1634 char *ptr2;
1635 CURLofft to_t;
1636 CURLofft from_t;
1637
1638 from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from);
1639 if(from_t == CURL_OFFT_FLOW) {
1640 return CURLE_RANGE_ERROR;
1641 }
1642 while(*ptr && (ISBLANK(*ptr) || (*ptr == '-')))
1643 ptr++;
1644 to_t = curlx_strtoofft(ptr, &ptr2, 10, &to);
1645 if(to_t == CURL_OFFT_FLOW) {
1646 return CURLE_RANGE_ERROR;
1647 }
1648 if((to_t == CURL_OFFT_INVAL) /* no "to" value given */
1649 || (to >= size)) {
1650 to = size - 1;
1651 }
1652 if(from_t) {
1653 /* from is relative to end of file */
1654 from = size - to;
1655 to = size - 1;
1656 }
1657 if(from > size) {
1658 failf(data, "Offset (%"
1659 CURL_FORMAT_CURL_OFF_T ") was beyond file size (%"
1660 CURL_FORMAT_CURL_OFF_T ")", from, size);
1661 return CURLE_BAD_DOWNLOAD_RESUME;
1662 }
1663 if(from > to) {
1664 from = to;
1665 size = 0;
1666 }
1667 else {
1668 if((to - from) == CURL_OFF_T_MAX)
1669 return CURLE_RANGE_ERROR;
1670 size = to - from + 1;
1671 }
1672
1673 rc = sftp_seek64(sshc->sftp_file, from);
1674 if(rc) {
1675 MOVE_TO_SFTP_CLOSE_STATE();
1676 break;
1677 }
1678 }
1679 data->req.size = size;
1680 data->req.maxdownload = size;
1681 Curl_pgrsSetDownloadSize(data, size);
1682 }
1683
1684 /* We can resume if we can seek to the resume position */
1685 if(data->state.resume_from) {
1686 if(data->state.resume_from < 0) {
1687 /* We're supposed to download the last abs(from) bytes */
1688 if((curl_off_t)size < -data->state.resume_from) {
1689 failf(data, "Offset (%"
1690 CURL_FORMAT_CURL_OFF_T ") was beyond file size (%"
1691 CURL_FORMAT_CURL_OFF_T ")",
1692 data->state.resume_from, size);
1693 return CURLE_BAD_DOWNLOAD_RESUME;
1694 }
1695 /* download from where? */
1696 data->state.resume_from += size;
1697 }
1698 else {
1699 if((curl_off_t)size < data->state.resume_from) {
1700 failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
1701 ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
1702 data->state.resume_from, size);
1703 return CURLE_BAD_DOWNLOAD_RESUME;
1704 }
1705 }
1706 /* Now store the number of bytes we are expected to download */
1707 data->req.size = size - data->state.resume_from;
1708 data->req.maxdownload = size - data->state.resume_from;
1709 Curl_pgrsSetDownloadSize(data,
1710 size - data->state.resume_from);
1711
1712 rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
1713 if(rc) {
1714 MOVE_TO_SFTP_CLOSE_STATE();
1715 break;
1716 }
1717 }
1718 }
1719
1720 /* Setup the actual download */
1721 if(data->req.size == 0) {
1722 /* no data to transfer */
1723 Curl_xfer_setup(data, -1, -1, FALSE, -1);
1724 infof(data, "File already completely downloaded");
1725 state(data, SSH_STOP);
1726 break;
1727 }
1728 Curl_xfer_setup(data, FIRSTSOCKET, data->req.size, FALSE, -1);
1729
1730 /* not set by Curl_xfer_setup to preserve keepon bits */
1731 conn->writesockfd = conn->sockfd;
1732
1733 /* we want to use the _receiving_ function even when the socket turns
1734 out writableable as the underlying libssh recv function will deal
1735 with both accordingly */
1736 data->state.select_bits = CURL_CSELECT_IN;
1737
1738 if(result) {
1739 /* this should never occur; the close state should be entered
1740 at the time the error occurs */
1741 state(data, SSH_SFTP_CLOSE);
1742 sshc->actualcode = result;
1743 }
1744 else {
1745 sshc->sftp_recv_state = 0;
1746 state(data, SSH_STOP);
1747 }
1748 break;
1749
1750 case SSH_SFTP_CLOSE:
1751 if(sshc->sftp_file) {
1752 sftp_close(sshc->sftp_file);
1753 sshc->sftp_file = NULL;
1754 }
1755 Curl_safefree(protop->path);
1756
1757 DEBUGF(infof(data, "SFTP DONE done"));
1758
1759 /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT
1760 After nextstate is executed, the control should come back to
1761 SSH_SFTP_CLOSE to pass the correct result back */
1762 if(sshc->nextstate != SSH_NO_STATE &&
1763 sshc->nextstate != SSH_SFTP_CLOSE) {
1764 state(data, sshc->nextstate);
1765 sshc->nextstate = SSH_SFTP_CLOSE;
1766 }
1767 else {
1768 state(data, SSH_STOP);
1769 result = sshc->actualcode;
1770 }
1771 break;
1772
1773 case SSH_SFTP_SHUTDOWN:
1774 /* during times we get here due to a broken transfer and then the
1775 sftp_handle might not have been taken down so make sure that is done
1776 before we proceed */
1777
1778 if(sshc->sftp_file) {
1779 sftp_close(sshc->sftp_file);
1780 sshc->sftp_file = NULL;
1781 }
1782
1783 if(sshc->sftp_session) {
1784 sftp_free(sshc->sftp_session);
1785 sshc->sftp_session = NULL;
1786 }
1787
1788 SSH_STRING_FREE_CHAR(sshc->homedir);
1789 data->state.most_recent_ftp_entrypath = NULL;
1790
1791 state(data, SSH_SESSION_DISCONNECT);
1792 break;
1793
1794 case SSH_SCP_TRANS_INIT:
1795 result = Curl_getworkingpath(data, sshc->homedir, &protop->path);
1796 if(result) {
1797 sshc->actualcode = result;
1798 state(data, SSH_STOP);
1799 break;
1800 }
1801
1802 /* Functions from the SCP subsystem cannot handle/return SSH_AGAIN */
1803 ssh_set_blocking(sshc->ssh_session, 1);
1804
1805 if(data->state.upload) {
1806 if(data->state.infilesize < 0) {
1807 failf(data, "SCP requires a known file size for upload");
1808 sshc->actualcode = CURLE_UPLOAD_FAILED;
1809 MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED);
1810 break;
1811 }
1812
1813 sshc->scp_session =
1814 ssh_scp_new(sshc->ssh_session, SSH_SCP_WRITE, protop->path);
1815 state(data, SSH_SCP_UPLOAD_INIT);
1816 }
1817 else {
1818 sshc->scp_session =
1819 ssh_scp_new(sshc->ssh_session, SSH_SCP_READ, protop->path);
1820 state(data, SSH_SCP_DOWNLOAD_INIT);
1821 }
1822
1823 if(!sshc->scp_session) {
1824 err_msg = ssh_get_error(sshc->ssh_session);
1825 failf(data, "%s", err_msg);
1826 MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED);
1827 }
1828
1829 break;
1830
1831 case SSH_SCP_UPLOAD_INIT:
1832
1833 rc = ssh_scp_init(sshc->scp_session);
1834 if(rc != SSH_OK) {
1835 err_msg = ssh_get_error(sshc->ssh_session);
1836 failf(data, "%s", err_msg);
1837 MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED);
1838 break;
1839 }
1840
1841 rc = ssh_scp_push_file(sshc->scp_session, protop->path,
1842 data->state.infilesize,
1843 (int)data->set.new_file_perms);
1844 if(rc != SSH_OK) {
1845 err_msg = ssh_get_error(sshc->ssh_session);
1846 failf(data, "%s", err_msg);
1847 MOVE_TO_ERROR_STATE(CURLE_UPLOAD_FAILED);
1848 break;
1849 }
1850
1851 /* upload data */
1852 Curl_xfer_setup(data, -1, data->req.size, FALSE, FIRSTSOCKET);
1853
1854 /* not set by Curl_xfer_setup to preserve keepon bits */
1855 conn->sockfd = conn->writesockfd;
1856
1857 /* store this original bitmask setup to use later on if we can't
1858 figure out a "real" bitmask */
1859 sshc->orig_waitfor = data->req.keepon;
1860
1861 /* we want to use the _sending_ function even when the socket turns
1862 out readable as the underlying libssh scp send function will deal
1863 with both accordingly */
1864 data->state.select_bits = CURL_CSELECT_OUT;
1865
1866 state(data, SSH_STOP);
1867
1868 break;
1869
1870 case SSH_SCP_DOWNLOAD_INIT:
1871
1872 rc = ssh_scp_init(sshc->scp_session);
1873 if(rc != SSH_OK) {
1874 err_msg = ssh_get_error(sshc->ssh_session);
1875 failf(data, "%s", err_msg);
1876 MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
1877 break;
1878 }
1879 state(data, SSH_SCP_DOWNLOAD);
1880 FALLTHROUGH();
1881
1882 case SSH_SCP_DOWNLOAD:{
1883 curl_off_t bytecount;
1884
1885 rc = ssh_scp_pull_request(sshc->scp_session);
1886 if(rc != SSH_SCP_REQUEST_NEWFILE) {
1887 err_msg = ssh_get_error(sshc->ssh_session);
1888 failf(data, "%s", err_msg);
1889 MOVE_TO_ERROR_STATE(CURLE_REMOTE_FILE_NOT_FOUND);
1890 break;
1891 }
1892
1893 /* download data */
1894 bytecount = ssh_scp_request_get_size(sshc->scp_session);
1895 data->req.maxdownload = (curl_off_t) bytecount;
1896 Curl_xfer_setup(data, FIRSTSOCKET, bytecount, FALSE, -1);
1897
1898 /* not set by Curl_xfer_setup to preserve keepon bits */
1899 conn->writesockfd = conn->sockfd;
1900
1901 /* we want to use the _receiving_ function even when the socket turns
1902 out writableable as the underlying libssh recv function will deal
1903 with both accordingly */
1904 data->state.select_bits = CURL_CSELECT_IN;
1905
1906 state(data, SSH_STOP);
1907 break;
1908 }
1909 case SSH_SCP_DONE:
1910 if(data->state.upload)
1911 state(data, SSH_SCP_SEND_EOF);
1912 else
1913 state(data, SSH_SCP_CHANNEL_FREE);
1914 break;
1915
1916 case SSH_SCP_SEND_EOF:
1917 if(sshc->scp_session) {
1918 rc = ssh_scp_close(sshc->scp_session);
1919 if(rc == SSH_AGAIN) {
1920 /* Currently the ssh_scp_close handles waiting for EOF in
1921 * blocking way.
1922 */
1923 break;
1924 }
1925 if(rc != SSH_OK) {
1926 infof(data, "Failed to close libssh scp channel: %s",
1927 ssh_get_error(sshc->ssh_session));
1928 }
1929 }
1930
1931 state(data, SSH_SCP_CHANNEL_FREE);
1932 break;
1933
1934 case SSH_SCP_CHANNEL_FREE:
1935 if(sshc->scp_session) {
1936 ssh_scp_free(sshc->scp_session);
1937 sshc->scp_session = NULL;
1938 }
1939 DEBUGF(infof(data, "SCP DONE phase complete"));
1940
1941 ssh_set_blocking(sshc->ssh_session, 0);
1942
1943 state(data, SSH_SESSION_DISCONNECT);
1944 FALLTHROUGH();
1945
1946 case SSH_SESSION_DISCONNECT:
1947 /* during weird times when we've been prematurely aborted, the channel
1948 is still alive when we reach this state and we MUST kill the channel
1949 properly first */
1950 if(sshc->scp_session) {
1951 ssh_scp_free(sshc->scp_session);
1952 sshc->scp_session = NULL;
1953 }
1954
1955 ssh_disconnect(sshc->ssh_session);
1956 if(!ssh_version(SSH_VERSION_INT(0, 10, 0))) {
1957 /* conn->sock[FIRSTSOCKET] is closed by ssh_disconnect behind our back,
1958 tell the connection to forget about it. This libssh
1959 bug is fixed in 0.10.0. */
1960 Curl_conn_forget_socket(data, FIRSTSOCKET);
1961 }
1962
1963 SSH_STRING_FREE_CHAR(sshc->homedir);
1964 data->state.most_recent_ftp_entrypath = NULL;
1965
1966 state(data, SSH_SESSION_FREE);
1967 FALLTHROUGH();
1968 case SSH_SESSION_FREE:
1969 if(sshc->ssh_session) {
1970 ssh_free(sshc->ssh_session);
1971 sshc->ssh_session = NULL;
1972 }
1973
1974 /* worst-case scenario cleanup */
1975
1976 DEBUGASSERT(sshc->ssh_session == NULL);
1977 DEBUGASSERT(sshc->scp_session == NULL);
1978
1979 if(sshc->readdir_tmp) {
1980 ssh_string_free_char(sshc->readdir_tmp);
1981 sshc->readdir_tmp = NULL;
1982 }
1983
1984 if(sshc->quote_attrs)
1985 sftp_attributes_free(sshc->quote_attrs);
1986
1987 if(sshc->readdir_attrs)
1988 sftp_attributes_free(sshc->readdir_attrs);
1989
1990 if(sshc->readdir_link_attrs)
1991 sftp_attributes_free(sshc->readdir_link_attrs);
1992
1993 if(sshc->privkey)
1994 ssh_key_free(sshc->privkey);
1995 if(sshc->pubkey)
1996 ssh_key_free(sshc->pubkey);
1997
1998 Curl_safefree(sshc->rsa_pub);
1999 Curl_safefree(sshc->rsa);
2000 Curl_safefree(sshc->quote_path1);
2001 Curl_safefree(sshc->quote_path2);
2002 Curl_dyn_free(&sshc->readdir_buf);
2003 Curl_safefree(sshc->readdir_linkPath);
2004 SSH_STRING_FREE_CHAR(sshc->homedir);
2005
2006 /* the code we are about to return */
2007 result = sshc->actualcode;
2008
2009 memset(sshc, 0, sizeof(struct ssh_conn));
2010
2011 connclose(conn, "SSH session free");
2012 sshc->state = SSH_SESSION_FREE; /* current */
2013 sshc->nextstate = SSH_NO_STATE;
2014 state(data, SSH_STOP);
2015 break;
2016
2017 case SSH_QUIT:
2018 default:
2019 /* internal error */
2020 sshc->nextstate = SSH_NO_STATE;
2021 state(data, SSH_STOP);
2022 break;
2023
2024 }
2025 } while(!rc && (sshc->state != SSH_STOP));
2026
2027
2028 if(rc == SSH_AGAIN) {
2029 /* we would block, we need to wait for the socket to be ready (in the
2030 right direction too)! */
2031 *block = TRUE;
2032 }
2033
2034 return result;
2035}
2036
2037
2038/* called by the multi interface to figure out what socket(s) to wait for and
2039 for what actions in the DO_DONE, PERFORM and WAITPERFORM states */
2040static int myssh_getsock(struct Curl_easy *data,
2041 struct connectdata *conn,
2042 curl_socket_t *sock)
2043{
2044 int bitmap = GETSOCK_BLANK;
2045 (void)data;
2046 sock[0] = conn->sock[FIRSTSOCKET];
2047
2048 if(conn->waitfor & KEEP_RECV)
2049 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
2050
2051 if(conn->waitfor & KEEP_SEND)
2052 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
2053
2054 if(!conn->waitfor)
2055 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
2056
2057 return bitmap;
2058}
2059
2060static void myssh_block2waitfor(struct connectdata *conn, bool block)
2061{
2062 struct ssh_conn *sshc = &conn->proto.sshc;
2063
2064 /* If it didn't block, or nothing was returned by ssh_get_poll_flags
2065 * have the original set */
2066 conn->waitfor = sshc->orig_waitfor;
2067
2068 if(block) {
2069 int dir = ssh_get_poll_flags(sshc->ssh_session);
2070 if(dir & SSH_READ_PENDING) {
2071 /* translate the libssh define bits into our own bit defines */
2072 conn->waitfor = KEEP_RECV;
2073 }
2074 else if(dir & SSH_WRITE_PENDING) {
2075 conn->waitfor = KEEP_SEND;
2076 }
2077 }
2078}
2079
2080/* called repeatedly until done from multi.c */
2081static CURLcode myssh_multi_statemach(struct Curl_easy *data,
2082 bool *done)
2083{
2084 struct connectdata *conn = data->conn;
2085 struct ssh_conn *sshc = &conn->proto.sshc;
2086 bool block; /* we store the status and use that to provide a ssh_getsock()
2087 implementation */
2088 CURLcode result = myssh_statemach_act(data, &block);
2089
2090 *done = (sshc->state == SSH_STOP) ? TRUE : FALSE;
2091 myssh_block2waitfor(conn, block);
2092
2093 return result;
2094}
2095
2096static CURLcode myssh_block_statemach(struct Curl_easy *data,
2097 bool disconnect)
2098{
2099 struct connectdata *conn = data->conn;
2100 struct ssh_conn *sshc = &conn->proto.sshc;
2101 CURLcode result = CURLE_OK;
2102
2103 while((sshc->state != SSH_STOP) && !result) {
2104 bool block;
2105 timediff_t left = 1000;
2106 struct curltime now = Curl_now();
2107
2108 result = myssh_statemach_act(data, &block);
2109 if(result)
2110 break;
2111
2112 if(!disconnect) {
2113 if(Curl_pgrsUpdate(data))
2114 return CURLE_ABORTED_BY_CALLBACK;
2115
2116 result = Curl_speedcheck(data, now);
2117 if(result)
2118 break;
2119
2120 left = Curl_timeleft(data, NULL, FALSE);
2121 if(left < 0) {
2122 failf(data, "Operation timed out");
2123 return CURLE_OPERATION_TIMEDOUT;
2124 }
2125 }
2126
2127 if(block) {
2128 curl_socket_t fd_read = conn->sock[FIRSTSOCKET];
2129 /* wait for the socket to become ready */
2130 (void) Curl_socket_check(fd_read, CURL_SOCKET_BAD,
2131 CURL_SOCKET_BAD, left > 1000 ? 1000 : left);
2132 }
2133
2134 }
2135
2136 return result;
2137}
2138
2139/*
2140 * SSH setup connection
2141 */
2142static CURLcode myssh_setup_connection(struct Curl_easy *data,
2143 struct connectdata *conn)
2144{
2145 struct SSHPROTO *ssh;
2146 struct ssh_conn *sshc = &conn->proto.sshc;
2147
2148 data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
2149 if(!ssh)
2150 return CURLE_OUT_OF_MEMORY;
2151 Curl_dyn_init(&sshc->readdir_buf, PATH_MAX * 2);
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 */
2163static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
2164{
2165 struct ssh_conn *ssh;
2166 CURLcode result;
2167 struct connectdata *conn = data->conn;
2168 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2169 int rc;
2170
2171 /* initialize per-handle data if not already */
2172 if(!data->req.p.ssh)
2173 myssh_setup_connection(data, conn);
2174
2175 /* We default to persistent connections. We set this already in this connect
2176 function to make the reuse 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();
2191 if(!ssh->ssh_session) {
2192 failf(data, "Failure initialising ssh session");
2193 return CURLE_FAILED_INIT;
2194 }
2195
2196 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 }
2201
2202 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') {
2215 infof(data, "User: %s", conn->user);
2216 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 }
2221 }
2222
2223 if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
2224 infof(data, "Known hosts: %s", data->set.str[STRING_SSH_KNOWNHOSTS]);
2225 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 }
2231 }
2232
2233 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 }
2241
2242 if(data->set.ssh_compression) {
2243 rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_COMPRESSION,
2244 "zlib,[email protected],none");
2245 if(rc != SSH_OK) {
2246 failf(data, "Could not set compression");
2247 return CURLE_FAILED_INIT;
2248 }
2249 }
2250
2251 ssh->privkey = NULL;
2252 ssh->pubkey = NULL;
2253
2254 if(data->set.str[STRING_SSH_PUBLIC_KEY]) {
2255 rc = ssh_pki_import_pubkey_file(data->set.str[STRING_SSH_PUBLIC_KEY],
2256 &ssh->pubkey);
2257 if(rc != SSH_OK) {
2258 failf(data, "Could not load public key file");
2259 return CURLE_FAILED_INIT;
2260 }
2261 }
2262
2263 /* we do not verify here, we do it at the state machine,
2264 * after connection */
2265
2266 state(data, SSH_INIT);
2267
2268 result = myssh_multi_statemach(data, done);
2269
2270 return result;
2271}
2272
2273/* called from multi.c while DOing */
2274static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done)
2275{
2276 CURLcode result;
2277
2278 result = myssh_multi_statemach(data, dophase_done);
2279
2280 if(*dophase_done) {
2281 DEBUGF(infof(data, "DO phase is complete"));
2282 }
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
2296CURLcode scp_perform(struct Curl_easy *data,
2297 bool *connected, bool *dophase_done)
2298{
2299 CURLcode result = CURLE_OK;
2300
2301 DEBUGF(infof(data, "DO phase starts"));
2302
2303 *dophase_done = FALSE; /* not done yet */
2304
2305 /* start the first command in the DO phase */
2306 state(data, SSH_SCP_TRANS_INIT);
2307
2308 result = myssh_multi_statemach(data, dophase_done);
2309
2310 *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
2311
2312 if(*dophase_done) {
2313 DEBUGF(infof(data, "DO phase is complete"));
2314 }
2315
2316 return result;
2317}
2318
2319static CURLcode myssh_do_it(struct Curl_easy *data, bool *done)
2320{
2321 CURLcode result;
2322 bool connected = 0;
2323 struct connectdata *conn = data->conn;
2324 struct ssh_conn *sshc = &conn->proto.sshc;
2325
2326 *done = FALSE; /* default to false */
2327
2328 data->req.size = -1; /* make sure this is unknown at this point */
2329
2330 sshc->actualcode = CURLE_OK; /* reset error code */
2331 sshc->secondCreateDirs = 0; /* reset the create dir attempt state
2332 variable */
2333
2334 Curl_pgrsSetUploadCounter(data, 0);
2335 Curl_pgrsSetDownloadCounter(data, 0);
2336 Curl_pgrsSetUploadSize(data, -1);
2337 Curl_pgrsSetDownloadSize(data, -1);
2338
2339 if(conn->handler->protocol & CURLPROTO_SCP)
2340 result = scp_perform(data, &connected, done);
2341 else
2342 result = sftp_perform(data, &connected, done);
2343
2344 return result;
2345}
2346
2347/* BLOCKING, but the function is using the state machine so the only reason
2348 this is still blocking is that the multi interface code has no support for
2349 disconnecting operations that takes a while */
2350static CURLcode scp_disconnect(struct Curl_easy *data,
2351 struct connectdata *conn,
2352 bool dead_connection)
2353{
2354 CURLcode result = CURLE_OK;
2355 struct ssh_conn *ssh = &conn->proto.sshc;
2356 (void) dead_connection;
2357
2358 if(ssh->ssh_session) {
2359 /* only if there's a session still around to use! */
2360
2361 state(data, SSH_SESSION_DISCONNECT);
2362
2363 result = myssh_block_statemach(data, TRUE);
2364 }
2365
2366 return result;
2367}
2368
2369/* generic done function for both SCP and SFTP called from their specific
2370 done functions */
2371static CURLcode myssh_done(struct Curl_easy *data, CURLcode status)
2372{
2373 CURLcode result = CURLE_OK;
2374 struct SSHPROTO *protop = data->req.p.ssh;
2375
2376 if(!status) {
2377 /* run the state-machine */
2378 result = myssh_block_statemach(data, FALSE);
2379 }
2380 else
2381 result = status;
2382
2383 if(protop)
2384 Curl_safefree(protop->path);
2385 if(Curl_pgrsDone(data))
2386 return CURLE_ABORTED_BY_CALLBACK;
2387
2388 data->req.keepon = 0; /* clear all bits */
2389 return result;
2390}
2391
2392
2393static CURLcode scp_done(struct Curl_easy *data, CURLcode status,
2394 bool premature)
2395{
2396 (void) premature; /* not used */
2397
2398 if(!status)
2399 state(data, SSH_SCP_DONE);
2400
2401 return myssh_done(data, status);
2402
2403}
2404
2405static ssize_t scp_send(struct Curl_easy *data, int sockindex,
2406 const void *mem, size_t len, CURLcode *err)
2407{
2408 int rc;
2409 struct connectdata *conn = data->conn;
2410 (void) sockindex; /* we only support SCP on the fixed known primary socket */
2411 (void) err;
2412
2413 rc = ssh_scp_write(conn->proto.sshc.scp_session, mem, len);
2414
2415#if 0
2416 /* The following code is misleading, mostly added as wishful thinking
2417 * that libssh at some point will implement non-blocking ssh_scp_write/read.
2418 * Currently rc can only be number of bytes read or SSH_ERROR. */
2419 myssh_block2waitfor(conn, (rc == SSH_AGAIN) ? TRUE : FALSE);
2420
2421 if(rc == SSH_AGAIN) {
2422 *err = CURLE_AGAIN;
2423 return 0;
2424 }
2425 else
2426#endif
2427 if(rc != SSH_OK) {
2428 *err = CURLE_SSH;
2429 return -1;
2430 }
2431
2432 return len;
2433}
2434
2435static ssize_t scp_recv(struct Curl_easy *data, int sockindex,
2436 char *mem, size_t len, CURLcode *err)
2437{
2438 ssize_t nread;
2439 struct connectdata *conn = data->conn;
2440 (void) err;
2441 (void) sockindex; /* we only support SCP on the fixed known primary socket */
2442
2443 /* libssh returns int */
2444 nread = ssh_scp_read(conn->proto.sshc.scp_session, mem, len);
2445
2446#if 0
2447 /* The following code is misleading, mostly added as wishful thinking
2448 * that libssh at some point will implement non-blocking ssh_scp_write/read.
2449 * Currently rc can only be SSH_OK or SSH_ERROR. */
2450
2451 myssh_block2waitfor(conn, (nread == SSH_AGAIN) ? TRUE : FALSE);
2452 if(nread == SSH_AGAIN) {
2453 *err = CURLE_AGAIN;
2454 nread = -1;
2455 }
2456#endif
2457
2458 return nread;
2459}
2460
2461/*
2462 * =============== SFTP ===============
2463 */
2464
2465/*
2466 ***********************************************************************
2467 *
2468 * sftp_perform()
2469 *
2470 * This is the actual DO function for SFTP. Get a file/directory according to
2471 * the options previously setup.
2472 */
2473
2474static
2475CURLcode sftp_perform(struct Curl_easy *data,
2476 bool *connected,
2477 bool *dophase_done)
2478{
2479 CURLcode result = CURLE_OK;
2480
2481 DEBUGF(infof(data, "DO phase starts"));
2482
2483 *dophase_done = FALSE; /* not done yet */
2484
2485 /* start the first command in the DO phase */
2486 state(data, SSH_SFTP_QUOTE_INIT);
2487
2488 /* run the state-machine */
2489 result = myssh_multi_statemach(data, dophase_done);
2490
2491 *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
2492
2493 if(*dophase_done) {
2494 DEBUGF(infof(data, "DO phase is complete"));
2495 }
2496
2497 return result;
2498}
2499
2500/* called from multi.c while DOing */
2501static CURLcode sftp_doing(struct Curl_easy *data,
2502 bool *dophase_done)
2503{
2504 CURLcode result = myssh_multi_statemach(data, dophase_done);
2505 if(*dophase_done) {
2506 DEBUGF(infof(data, "DO phase is complete"));
2507 }
2508 return result;
2509}
2510
2511/* BLOCKING, but the function is using the state machine so the only reason
2512 this is still blocking is that the multi interface code has no support for
2513 disconnecting operations that takes a while */
2514static CURLcode sftp_disconnect(struct Curl_easy *data,
2515 struct connectdata *conn,
2516 bool dead_connection)
2517{
2518 CURLcode result = CURLE_OK;
2519 (void) dead_connection;
2520
2521 DEBUGF(infof(data, "SSH DISCONNECT starts now"));
2522
2523 if(conn->proto.sshc.ssh_session) {
2524 /* only if there's a session still around to use! */
2525 state(data, SSH_SFTP_SHUTDOWN);
2526 result = myssh_block_statemach(data, TRUE);
2527 }
2528
2529 DEBUGF(infof(data, "SSH DISCONNECT is done"));
2530
2531 return result;
2532
2533}
2534
2535static CURLcode sftp_done(struct Curl_easy *data, CURLcode status,
2536 bool premature)
2537{
2538 struct connectdata *conn = data->conn;
2539 struct ssh_conn *sshc = &conn->proto.sshc;
2540
2541 if(!status) {
2542 /* Post quote commands are executed after the SFTP_CLOSE state to avoid
2543 errors that could happen due to open file handles during POSTQUOTE
2544 operation */
2545 if(!premature && data->set.postquote && !conn->bits.retry)
2546 sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT;
2547 state(data, SSH_SFTP_CLOSE);
2548 }
2549 return myssh_done(data, status);
2550}
2551
2552/* return number of sent bytes */
2553static ssize_t sftp_send(struct Curl_easy *data, int sockindex,
2554 const void *mem, size_t len, CURLcode *err)
2555{
2556 ssize_t nwrite;
2557 struct connectdata *conn = data->conn;
2558 (void)sockindex;
2559
2560 /* limit the writes to the maximum specified in Section 3 of
2561 * https://datatracker.ietf.org/doc/html/draft-ietf-secsh-filexfer-02
2562 */
2563 if(len > 32768)
2564 len = 32768;
2565
2566 nwrite = sftp_write(conn->proto.sshc.sftp_file, mem, len);
2567
2568 myssh_block2waitfor(conn, FALSE);
2569
2570#if 0 /* not returned by libssh on write */
2571 if(nwrite == SSH_AGAIN) {
2572 *err = CURLE_AGAIN;
2573 nwrite = 0;
2574 }
2575 else
2576#endif
2577 if(nwrite < 0) {
2578 *err = CURLE_SSH;
2579 nwrite = -1;
2580 }
2581
2582 return nwrite;
2583}
2584
2585/*
2586 * Return number of received (decrypted) bytes
2587 * or <0 on error
2588 */
2589static ssize_t sftp_recv(struct Curl_easy *data, int sockindex,
2590 char *mem, size_t len, CURLcode *err)
2591{
2592 ssize_t nread;
2593 struct connectdata *conn = data->conn;
2594 (void)sockindex;
2595
2596 DEBUGASSERT(len < CURL_MAX_READ_SIZE);
2597
2598 switch(conn->proto.sshc.sftp_recv_state) {
2599 case 0:
2600 conn->proto.sshc.sftp_file_index =
2601 sftp_async_read_begin(conn->proto.sshc.sftp_file,
2602 (uint32_t)len);
2603 if(conn->proto.sshc.sftp_file_index < 0) {
2604 *err = CURLE_RECV_ERROR;
2605 return -1;
2606 }
2607
2608 FALLTHROUGH();
2609 case 1:
2610 conn->proto.sshc.sftp_recv_state = 1;
2611
2612 nread = sftp_async_read(conn->proto.sshc.sftp_file,
2613 mem, (uint32_t)len,
2614 conn->proto.sshc.sftp_file_index);
2615
2616 myssh_block2waitfor(conn, (nread == SSH_AGAIN)?TRUE:FALSE);
2617
2618 if(nread == SSH_AGAIN) {
2619 *err = CURLE_AGAIN;
2620 return -1;
2621 }
2622 else if(nread < 0) {
2623 *err = CURLE_RECV_ERROR;
2624 return -1;
2625 }
2626
2627 conn->proto.sshc.sftp_recv_state = 0;
2628 return nread;
2629
2630 default:
2631 /* we never reach here */
2632 return -1;
2633 }
2634}
2635
2636static void sftp_quote(struct Curl_easy *data)
2637{
2638 const char *cp;
2639 struct connectdata *conn = data->conn;
2640 struct SSHPROTO *protop = data->req.p.ssh;
2641 struct ssh_conn *sshc = &conn->proto.sshc;
2642 CURLcode result;
2643
2644 /*
2645 * Support some of the "FTP" commands
2646 */
2647 char *cmd = sshc->quote_item->data;
2648 sshc->acceptfail = FALSE;
2649
2650 /* if a command starts with an asterisk, which a legal SFTP command never
2651 can, the command will be allowed to fail without it causing any
2652 aborts or cancels etc. It will cause libcurl to act as if the command
2653 is successful, whatever the server responds. */
2654
2655 if(cmd[0] == '*') {
2656 cmd++;
2657 sshc->acceptfail = TRUE;
2658 }
2659
2660 if(strcasecompare("pwd", cmd)) {
2661 /* output debug output if that is requested */
2662 char *tmp = aprintf("257 \"%s\" is current directory.\n",
2663 protop->path);
2664 if(!tmp) {
2665 sshc->actualcode = CURLE_OUT_OF_MEMORY;
2666 state(data, SSH_SFTP_CLOSE);
2667 sshc->nextstate = SSH_NO_STATE;
2668 return;
2669 }
2670 Curl_debug(data, CURLINFO_HEADER_OUT, (char *) "PWD\n", 4);
2671 Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp));
2672
2673 /* this sends an FTP-like "header" to the header callback so that the
2674 current directory can be read very similar to how it is read when
2675 using ordinary FTP. */
2676 result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
2677 free(tmp);
2678 if(result) {
2679 state(data, SSH_SFTP_CLOSE);
2680 sshc->nextstate = SSH_NO_STATE;
2681 sshc->actualcode = result;
2682 }
2683 else
2684 state(data, SSH_SFTP_NEXT_QUOTE);
2685 return;
2686 }
2687
2688 /*
2689 * the arguments following the command must be separated from the
2690 * command with a space so we can check for it unconditionally
2691 */
2692 cp = strchr(cmd, ' ');
2693 if(!cp) {
2694 failf(data, "Syntax error in SFTP command. Supply parameter(s)");
2695 state(data, SSH_SFTP_CLOSE);
2696 sshc->nextstate = SSH_NO_STATE;
2697 sshc->actualcode = CURLE_QUOTE_ERROR;
2698 return;
2699 }
2700
2701 /*
2702 * also, every command takes at least one argument so we get that
2703 * first argument right now
2704 */
2705 result = Curl_get_pathname(&cp, &sshc->quote_path1, sshc->homedir);
2706 if(result) {
2707 if(result == CURLE_OUT_OF_MEMORY)
2708 failf(data, "Out of memory");
2709 else
2710 failf(data, "Syntax error: Bad first parameter");
2711 state(data, SSH_SFTP_CLOSE);
2712 sshc->nextstate = SSH_NO_STATE;
2713 sshc->actualcode = result;
2714 return;
2715 }
2716
2717 /*
2718 * SFTP is a binary protocol, so we don't send text commands
2719 * to the server. Instead, we scan for commands used by
2720 * OpenSSH's sftp program and call the appropriate libssh
2721 * functions.
2722 */
2723 if(strncasecompare(cmd, "chgrp ", 6) ||
2724 strncasecompare(cmd, "chmod ", 6) ||
2725 strncasecompare(cmd, "chown ", 6) ||
2726 strncasecompare(cmd, "atime ", 6) ||
2727 strncasecompare(cmd, "mtime ", 6)) {
2728 /* attribute change */
2729
2730 /* sshc->quote_path1 contains the mode to set */
2731 /* get the destination */
2732 result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
2733 if(result) {
2734 if(result == CURLE_OUT_OF_MEMORY)
2735 failf(data, "Out of memory");
2736 else
2737 failf(data, "Syntax error in chgrp/chmod/chown/atime/mtime: "
2738 "Bad second parameter");
2739 Curl_safefree(sshc->quote_path1);
2740 state(data, SSH_SFTP_CLOSE);
2741 sshc->nextstate = SSH_NO_STATE;
2742 sshc->actualcode = result;
2743 return;
2744 }
2745 sshc->quote_attrs = NULL;
2746 state(data, SSH_SFTP_QUOTE_STAT);
2747 return;
2748 }
2749 if(strncasecompare(cmd, "ln ", 3) ||
2750 strncasecompare(cmd, "symlink ", 8)) {
2751 /* symbolic linking */
2752 /* sshc->quote_path1 is the source */
2753 /* get the destination */
2754 result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
2755 if(result) {
2756 if(result == CURLE_OUT_OF_MEMORY)
2757 failf(data, "Out of memory");
2758 else
2759 failf(data, "Syntax error in ln/symlink: Bad second parameter");
2760 Curl_safefree(sshc->quote_path1);
2761 state(data, SSH_SFTP_CLOSE);
2762 sshc->nextstate = SSH_NO_STATE;
2763 sshc->actualcode = result;
2764 return;
2765 }
2766 state(data, SSH_SFTP_QUOTE_SYMLINK);
2767 return;
2768 }
2769 else if(strncasecompare(cmd, "mkdir ", 6)) {
2770 /* create dir */
2771 state(data, SSH_SFTP_QUOTE_MKDIR);
2772 return;
2773 }
2774 else if(strncasecompare(cmd, "rename ", 7)) {
2775 /* rename file */
2776 /* first param is the source path */
2777 /* second param is the dest. path */
2778 result = Curl_get_pathname(&cp, &sshc->quote_path2, sshc->homedir);
2779 if(result) {
2780 if(result == CURLE_OUT_OF_MEMORY)
2781 failf(data, "Out of memory");
2782 else
2783 failf(data, "Syntax error in rename: Bad second parameter");
2784 Curl_safefree(sshc->quote_path1);
2785 state(data, SSH_SFTP_CLOSE);
2786 sshc->nextstate = SSH_NO_STATE;
2787 sshc->actualcode = result;
2788 return;
2789 }
2790 state(data, SSH_SFTP_QUOTE_RENAME);
2791 return;
2792 }
2793 else if(strncasecompare(cmd, "rmdir ", 6)) {
2794 /* delete dir */
2795 state(data, SSH_SFTP_QUOTE_RMDIR);
2796 return;
2797 }
2798 else if(strncasecompare(cmd, "rm ", 3)) {
2799 state(data, SSH_SFTP_QUOTE_UNLINK);
2800 return;
2801 }
2802#ifdef HAS_STATVFS_SUPPORT
2803 else if(strncasecompare(cmd, "statvfs ", 8)) {
2804 state(data, SSH_SFTP_QUOTE_STATVFS);
2805 return;
2806 }
2807#endif
2808
2809 failf(data, "Unknown SFTP command");
2810 Curl_safefree(sshc->quote_path1);
2811 Curl_safefree(sshc->quote_path2);
2812 state(data, SSH_SFTP_CLOSE);
2813 sshc->nextstate = SSH_NO_STATE;
2814 sshc->actualcode = CURLE_QUOTE_ERROR;
2815}
2816
2817static void sftp_quote_stat(struct Curl_easy *data)
2818{
2819 struct connectdata *conn = data->conn;
2820 struct ssh_conn *sshc = &conn->proto.sshc;
2821 char *cmd = sshc->quote_item->data;
2822 sshc->acceptfail = FALSE;
2823
2824 /* if a command starts with an asterisk, which a legal SFTP command never
2825 can, the command will be allowed to fail without it causing any
2826 aborts or cancels etc. It will cause libcurl to act as if the command
2827 is successful, whatever the server responds. */
2828
2829 if(cmd[0] == '*') {
2830 cmd++;
2831 sshc->acceptfail = TRUE;
2832 }
2833
2834 /* We read the file attributes, store them in sshc->quote_attrs
2835 * and modify them accordingly to command. Then we switch to
2836 * QUOTE_SETSTAT state to write new ones.
2837 */
2838
2839 if(sshc->quote_attrs)
2840 sftp_attributes_free(sshc->quote_attrs);
2841 sshc->quote_attrs = sftp_stat(sshc->sftp_session, sshc->quote_path2);
2842 if(!sshc->quote_attrs) {
2843 Curl_safefree(sshc->quote_path1);
2844 Curl_safefree(sshc->quote_path2);
2845 failf(data, "Attempt to get SFTP stats failed: %d",
2846 sftp_get_error(sshc->sftp_session));
2847 state(data, SSH_SFTP_CLOSE);
2848 sshc->nextstate = SSH_NO_STATE;
2849 sshc->actualcode = CURLE_QUOTE_ERROR;
2850 return;
2851 }
2852
2853 /* Now set the new attributes... */
2854 if(strncasecompare(cmd, "chgrp", 5)) {
2855 sshc->quote_attrs->gid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10);
2856 if(sshc->quote_attrs->gid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
2857 !sshc->acceptfail) {
2858 Curl_safefree(sshc->quote_path1);
2859 Curl_safefree(sshc->quote_path2);
2860 failf(data, "Syntax error: chgrp gid not a number");
2861 state(data, SSH_SFTP_CLOSE);
2862 sshc->nextstate = SSH_NO_STATE;
2863 sshc->actualcode = CURLE_QUOTE_ERROR;
2864 return;
2865 }
2866 sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID;
2867 }
2868 else if(strncasecompare(cmd, "chmod", 5)) {
2869 mode_t perms;
2870 perms = (mode_t)strtoul(sshc->quote_path1, NULL, 8);
2871 /* permissions are octal */
2872 if(perms == 0 && !ISDIGIT(sshc->quote_path1[0])) {
2873 Curl_safefree(sshc->quote_path1);
2874 Curl_safefree(sshc->quote_path2);
2875 failf(data, "Syntax error: chmod permissions not a number");
2876 state(data, SSH_SFTP_CLOSE);
2877 sshc->nextstate = SSH_NO_STATE;
2878 sshc->actualcode = CURLE_QUOTE_ERROR;
2879 return;
2880 }
2881 sshc->quote_attrs->permissions = perms;
2882 sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
2883 }
2884 else if(strncasecompare(cmd, "chown", 5)) {
2885 sshc->quote_attrs->uid = (uint32_t)strtoul(sshc->quote_path1, NULL, 10);
2886 if(sshc->quote_attrs->uid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
2887 !sshc->acceptfail) {
2888 Curl_safefree(sshc->quote_path1);
2889 Curl_safefree(sshc->quote_path2);
2890 failf(data, "Syntax error: chown uid not a number");
2891 state(data, SSH_SFTP_CLOSE);
2892 sshc->nextstate = SSH_NO_STATE;
2893 sshc->actualcode = CURLE_QUOTE_ERROR;
2894 return;
2895 }
2896 sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID;
2897 }
2898 else if(strncasecompare(cmd, "atime", 5) ||
2899 strncasecompare(cmd, "mtime", 5)) {
2900 time_t date = Curl_getdate_capped(sshc->quote_path1);
2901 bool fail = FALSE;
2902 if(date == -1) {
2903 failf(data, "incorrect date format for %.*s", 5, cmd);
2904 fail = TRUE;
2905 }
2906#if SIZEOF_TIME_T > 4
2907 else if(date > 0xffffffff) {
2908 failf(data, "date overflow");
2909 fail = TRUE; /* avoid setting a capped time */
2910 }
2911#endif
2912 if(fail) {
2913 Curl_safefree(sshc->quote_path1);
2914 Curl_safefree(sshc->quote_path2);
2915 state(data, SSH_SFTP_CLOSE);
2916 sshc->nextstate = SSH_NO_STATE;
2917 sshc->actualcode = CURLE_QUOTE_ERROR;
2918 return;
2919 }
2920 if(strncasecompare(cmd, "atime", 5))
2921 sshc->quote_attrs->atime = (uint32_t)date;
2922 else /* mtime */
2923 sshc->quote_attrs->mtime = (uint32_t)date;
2924
2925 sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_ACMODTIME;
2926 }
2927
2928 /* Now send the completed structure... */
2929 state(data, SSH_SFTP_QUOTE_SETSTAT);
2930 return;
2931}
2932
2933CURLcode Curl_ssh_init(void)
2934{
2935 if(ssh_init()) {
2936 DEBUGF(fprintf(stderr, "Error: libssh_init failed\n"));
2937 return CURLE_FAILED_INIT;
2938 }
2939 return CURLE_OK;
2940}
2941
2942void Curl_ssh_cleanup(void)
2943{
2944 (void)ssh_finalize();
2945}
2946
2947void Curl_ssh_version(char *buffer, size_t buflen)
2948{
2949 (void)msnprintf(buffer, buflen, "libssh/%s", ssh_version(0));
2950}
2951
2952#endif /* USE_LIBSSH */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette