VirtualBox

source: vbox/trunk/src/libs/curl-8.0.1/lib/vssh/libssh.c@ 99797

Last change on this file since 99797 was 99344, checked in by vboxsync, 2 years ago

curl-8.0.1: Applied and adjusted our curl changes to 7.87.0 bugref:10417

  • Property svn:eol-style set to native
File size: 85.5 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#include <libssh/libssh.h>
35#include <libssh/sftp.h>
36
37#ifdef HAVE_NETINET_IN_H
38#include <netinet/in.h>
39#endif
40#ifdef HAVE_ARPA_INET_H
41#include <arpa/inet.h>
42#endif
43#ifdef HAVE_UTSNAME_H
44#include <sys/utsname.h>
45#endif
46#ifdef HAVE_NETDB_H
47#include <netdb.h>
48#endif
49#ifdef __VMS
50#include <in.h>
51#include <inet.h>
52#endif
53
54#include <curl/curl.h>
55#include "urldata.h"
56#include "sendf.h"
57#include "hostip.h"
58#include "progress.h"
59#include "transfer.h"
60#include "escape.h"
61#include "http.h" /* for HTTP proxy tunnel stuff */
62#include "ssh.h"
63#include "url.h"
64#include "speedcheck.h"
65#include "getinfo.h"
66#include "strdup.h"
67#include "strcase.h"
68#include "vtls/vtls.h"
69#include "cfilters.h"
70#include "connect.h"
71#include "inet_ntop.h"
72#include "parsedate.h" /* for the week day and month names */
73#include "sockaddr.h" /* required for Curl_sockaddr_storage */
74#include "strtoofft.h"
75#include "multiif.h"
76#include "select.h"
77#include "warnless.h"
78#include "curl_path.h"
79
80#ifdef HAVE_SYS_STAT_H
81#include <sys/stat.h>
82#endif
83#ifdef HAVE_UNISTD_H
84#include <unistd.h>
85#endif
86#ifdef HAVE_FCNTL_H
87#include <fcntl.h>
88#endif
89
90/* The last 3 #include files should be in this order */
91#include "curl_printf.h"
92#include "curl_memory.h"
93#include "memdebug.h"
94
95/* in 0.10.0 or later, ignore deprecated warnings */
96#if defined(__GNUC__) && \
97 (LIBSSH_VERSION_MINOR >= 10) || \
98 (LIBSSH_VERSION_MAJOR > 0)
99#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
100#endif
101
102/* A recent macro provided by libssh. Or make our own. */
103#ifndef SSH_STRING_FREE_CHAR
104#define SSH_STRING_FREE_CHAR(x) \
105 do { \
106 if(x) { \
107 ssh_string_free_char(x); \
108 x = NULL; \
109 } \
110 } while(0)
111#endif
112
113/* These stat values may not be the same as the user's S_IFMT / S_IFLNK */
114#ifndef SSH_S_IFMT
115#define SSH_S_IFMT 00170000
116#endif
117#ifndef SSH_S_IFLNK
118#define SSH_S_IFLNK 0120000
119#endif
120
121/* Local functions: */
122static CURLcode myssh_connect(struct Curl_easy *data, bool *done);
123static CURLcode myssh_multi_statemach(struct Curl_easy *data,
124 bool *done);
125static CURLcode myssh_do_it(struct Curl_easy *data, bool *done);
126
127static CURLcode scp_done(struct Curl_easy *data,
128 CURLcode, bool premature);
129static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done);
130static CURLcode scp_disconnect(struct Curl_easy *data,
131 struct connectdata *conn,
132 bool dead_connection);
133
134static CURLcode sftp_done(struct Curl_easy *data,
135 CURLcode, bool premature);
136static CURLcode sftp_doing(struct Curl_easy *data,
137 bool *dophase_done);
138static CURLcode sftp_disconnect(struct Curl_easy *data,
139 struct connectdata *conn,
140 bool dead);
141static
142CURLcode sftp_perform(struct Curl_easy *data,
143 bool *connected,
144 bool *dophase_done);
145
146static void sftp_quote(struct Curl_easy *data);
147static void sftp_quote_stat(struct Curl_easy *data);
148static int myssh_getsock(struct Curl_easy *data,
149 struct connectdata *conn, curl_socket_t *sock);
150
151static CURLcode myssh_setup_connection(struct Curl_easy *data,
152 struct connectdata *conn);
153
154/*
155 * SCP protocol handler.
156 */
157
158const struct Curl_handler Curl_handler_scp = {
159 "SCP", /* scheme */
160 myssh_setup_connection, /* setup_connection */
161 myssh_do_it, /* do_it */
162 scp_done, /* done */
163 ZERO_NULL, /* do_more */
164 myssh_connect, /* connect_it */
165 myssh_multi_statemach, /* connecting */
166 scp_doing, /* doing */
167 myssh_getsock, /* proto_getsock */
168 myssh_getsock, /* doing_getsock */
169 ZERO_NULL, /* domore_getsock */
170 myssh_getsock, /* perform_getsock */
171 scp_disconnect, /* disconnect */
172 ZERO_NULL, /* readwrite */
173 ZERO_NULL, /* connection_check */
174 ZERO_NULL, /* attach connection */
175 PORT_SSH, /* defport */
176 CURLPROTO_SCP, /* protocol */
177 CURLPROTO_SCP, /* family */
178 PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION | PROTOPT_NOURLQUERY /* flags */
179};
180
181/*
182 * SFTP protocol handler.
183 */
184
185const struct Curl_handler Curl_handler_sftp = {
186 "SFTP", /* scheme */
187 myssh_setup_connection, /* setup_connection */
188 myssh_do_it, /* do_it */
189 sftp_done, /* done */
190 ZERO_NULL, /* do_more */
191 myssh_connect, /* connect_it */
192 myssh_multi_statemach, /* connecting */
193 sftp_doing, /* doing */
194 myssh_getsock, /* proto_getsock */
195 myssh_getsock, /* doing_getsock */
196 ZERO_NULL, /* domore_getsock */
197 myssh_getsock, /* perform_getsock */
198 sftp_disconnect, /* disconnect */
199 ZERO_NULL, /* readwrite */
200 ZERO_NULL, /* connection_check */
201 ZERO_NULL, /* attach connection */
202 PORT_SSH, /* defport */
203 CURLPROTO_SFTP, /* protocol */
204 CURLPROTO_SFTP, /* family */
205 PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
206 | PROTOPT_NOURLQUERY /* flags */
207};
208
209static CURLcode sftp_error_to_CURLE(int err)
210{
211 switch(err) {
212 case SSH_FX_OK:
213 return CURLE_OK;
214
215 case SSH_FX_NO_SUCH_FILE:
216 case SSH_FX_NO_SUCH_PATH:
217 return CURLE_REMOTE_FILE_NOT_FOUND;
218
219 case SSH_FX_PERMISSION_DENIED:
220 case SSH_FX_WRITE_PROTECT:
221 return CURLE_REMOTE_ACCESS_DENIED;
222
223 case SSH_FX_FILE_ALREADY_EXISTS:
224 return CURLE_REMOTE_FILE_EXISTS;
225
226 default:
227 break;
228 }
229
230 return CURLE_SSH;
231}
232
233#ifndef DEBUGBUILD
234#define state(x,y) mystate(x,y)
235#else
236#define state(x,y) mystate(x,y, __LINE__)
237#endif
238
239/*
240 * SSH State machine related code
241 */
242/* This is the ONLY way to change SSH state! */
243static void mystate(struct Curl_easy *data, sshstate nowstate
244#ifdef DEBUGBUILD
245 , int lineno
246#endif
247 )
248{
249 struct connectdata *conn = data->conn;
250 struct ssh_conn *sshc = &conn->proto.sshc;
251#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
252 /* for debug purposes */
253 static const char *const names[] = {
254 "SSH_STOP",
255 "SSH_INIT",
256 "SSH_S_STARTUP",
257 "SSH_HOSTKEY",
258 "SSH_AUTHLIST",
259 "SSH_AUTH_PKEY_INIT",
260 "SSH_AUTH_PKEY",
261 "SSH_AUTH_PASS_INIT",
262 "SSH_AUTH_PASS",
263 "SSH_AUTH_AGENT_INIT",
264 "SSH_AUTH_AGENT_LIST",
265 "SSH_AUTH_AGENT",
266 "SSH_AUTH_HOST_INIT",
267 "SSH_AUTH_HOST",
268 "SSH_AUTH_KEY_INIT",
269 "SSH_AUTH_KEY",
270 "SSH_AUTH_GSSAPI",
271 "SSH_AUTH_DONE",
272 "SSH_SFTP_INIT",
273 "SSH_SFTP_REALPATH",
274 "SSH_SFTP_QUOTE_INIT",
275 "SSH_SFTP_POSTQUOTE_INIT",
276 "SSH_SFTP_QUOTE",
277 "SSH_SFTP_NEXT_QUOTE",
278 "SSH_SFTP_QUOTE_STAT",
279 "SSH_SFTP_QUOTE_SETSTAT",
280 "SSH_SFTP_QUOTE_SYMLINK",
281 "SSH_SFTP_QUOTE_MKDIR",
282 "SSH_SFTP_QUOTE_RENAME",
283 "SSH_SFTP_QUOTE_RMDIR",
284 "SSH_SFTP_QUOTE_UNLINK",
285 "SSH_SFTP_QUOTE_STATVFS",
286 "SSH_SFTP_GETINFO",
287 "SSH_SFTP_FILETIME",
288 "SSH_SFTP_TRANS_INIT",
289 "SSH_SFTP_UPLOAD_INIT",
290 "SSH_SFTP_CREATE_DIRS_INIT",
291 "SSH_SFTP_CREATE_DIRS",
292 "SSH_SFTP_CREATE_DIRS_MKDIR",
293 "SSH_SFTP_READDIR_INIT",
294 "SSH_SFTP_READDIR",
295 "SSH_SFTP_READDIR_LINK",
296 "SSH_SFTP_READDIR_BOTTOM",
297 "SSH_SFTP_READDIR_DONE",
298 "SSH_SFTP_DOWNLOAD_INIT",
299 "SSH_SFTP_DOWNLOAD_STAT",
300 "SSH_SFTP_CLOSE",
301 "SSH_SFTP_SHUTDOWN",
302 "SSH_SCP_TRANS_INIT",
303 "SSH_SCP_UPLOAD_INIT",
304 "SSH_SCP_DOWNLOAD_INIT",
305 "SSH_SCP_DOWNLOAD",
306 "SSH_SCP_DONE",
307 "SSH_SCP_SEND_EOF",
308 "SSH_SCP_WAIT_EOF",
309 "SSH_SCP_WAIT_CLOSE",
310 "SSH_SCP_CHANNEL_FREE",
311 "SSH_SESSION_DISCONNECT",
312 "SSH_SESSION_FREE",
313 "QUIT"
314 };
315
316
317 if(sshc->state != nowstate) {
318 infof(data, "SSH %p state change from %s to %s (line %d)",
319 (void *) sshc, names[sshc->state], names[nowstate],
320 lineno);
321 }
322#endif
323
324 sshc->state = nowstate;
325}
326
327/* Multiple options:
328 * 1. data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] is set with an MD5
329 * hash (90s style auth, not sure we should have it here)
330 * 2. data->set.ssh_keyfunc callback is set. Then we do trust on first
331 * use. We even save on knownhosts if CURLKHSTAT_FINE_ADD_TO_FILE
332 * is returned by it.
333 * 3. none of the above. We only accept if it is present on known hosts.
334 *
335 * Returns SSH_OK or SSH_ERROR.
336 */
337static int myssh_is_known(struct Curl_easy *data)
338{
339 int rc;
340 struct connectdata *conn = data->conn;
341 struct ssh_conn *sshc = &conn->proto.sshc;
342 ssh_key pubkey;
343 size_t hlen;
344 unsigned char *hash = NULL;
345 char *found_base64 = NULL;
346 char *known_base64 = NULL;
347 int vstate;
348 enum curl_khmatch keymatch;
349 struct curl_khkey foundkey;
350 struct curl_khkey *knownkeyp = NULL;
351 curl_sshkeycallback func =
352 data->set.ssh_keyfunc;
353
354#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
355 struct ssh_knownhosts_entry *knownhostsentry = NULL;
356 struct curl_khkey knownkey;
357#endif
358
359#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0)
360 rc = ssh_get_server_publickey(sshc->ssh_session, &pubkey);
361#else
362 rc = ssh_get_publickey(sshc->ssh_session, &pubkey);
363#endif
364 if(rc != SSH_OK)
365 return rc;
366
367 if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) {
368 int i;
369 char md5buffer[33];
370 const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5];
371
372 rc = ssh_get_publickey_hash(pubkey, SSH_PUBLICKEY_HASH_MD5,
373 &hash, &hlen);
374 if(rc != SSH_OK || hlen != 16) {
375 failf(data,
376 "Denied establishing ssh session: md5 fingerprint not available");
377 goto cleanup;
378 }
379
380 for(i = 0; i < 16; i++)
381 msnprintf(&md5buffer[i*2], 3, "%02x", (unsigned char)hash[i]);
382
383 infof(data, "SSH MD5 fingerprint: %s", md5buffer);
384
385 if(!strcasecompare(md5buffer, pubkey_md5)) {
386 failf(data,
387 "Denied establishing ssh session: mismatch md5 fingerprint. "
388 "Remote %s is not equal to %s", md5buffer, pubkey_md5);
389 rc = SSH_ERROR;
390 goto cleanup;
391 }
392
393 rc = SSH_OK;
394 goto cleanup;
395 }
396
397 if(data->set.ssl.primary.verifyhost != TRUE) {
398 rc = SSH_OK;
399 goto cleanup;
400 }
401
402#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
403 /* Get the known_key from the known hosts file */
404 vstate = ssh_session_get_known_hosts_entry(sshc->ssh_session,
405 &knownhostsentry);
406
407 /* Case an entry was found in a known hosts file */
408 if(knownhostsentry) {
409 if(knownhostsentry->publickey) {
410 rc = ssh_pki_export_pubkey_base64(knownhostsentry->publickey,
411 &known_base64);
412 if(rc != SSH_OK) {
413 goto cleanup;
414 }
415 knownkey.key = known_base64;
416 knownkey.len = strlen(known_base64);
417
418 switch(ssh_key_type(knownhostsentry->publickey)) {
419 case SSH_KEYTYPE_RSA:
420 knownkey.keytype = CURLKHTYPE_RSA;
421 break;
422 case SSH_KEYTYPE_RSA1:
423 knownkey.keytype = CURLKHTYPE_RSA1;
424 break;
425 case SSH_KEYTYPE_ECDSA:
426 case SSH_KEYTYPE_ECDSA_P256:
427 case SSH_KEYTYPE_ECDSA_P384:
428 case SSH_KEYTYPE_ECDSA_P521:
429 knownkey.keytype = CURLKHTYPE_ECDSA;
430 break;
431 case SSH_KEYTYPE_ED25519:
432 knownkey.keytype = CURLKHTYPE_ED25519;
433 break;
434 case SSH_KEYTYPE_DSS:
435 knownkey.keytype = CURLKHTYPE_DSS;
436 break;
437 default:
438 rc = SSH_ERROR;
439 goto cleanup;
440 }
441 knownkeyp = &knownkey;
442 }
443 }
444
445 switch(vstate) {
446 case SSH_KNOWN_HOSTS_OK:
447 keymatch = CURLKHMATCH_OK;
448 break;
449 case SSH_KNOWN_HOSTS_OTHER:
450 /* fallthrough */
451 case SSH_KNOWN_HOSTS_NOT_FOUND:
452 /* fallthrough */
453 case SSH_KNOWN_HOSTS_UNKNOWN:
454 /* fallthrough */
455 case SSH_KNOWN_HOSTS_ERROR:
456 keymatch = CURLKHMATCH_MISSING;
457 break;
458 default:
459 keymatch = CURLKHMATCH_MISMATCH;
460 break;
461 }
462
463#else
464 vstate = ssh_is_server_known(sshc->ssh_session);
465 switch(vstate) {
466 case SSH_SERVER_KNOWN_OK:
467 keymatch = CURLKHMATCH_OK;
468 break;
469 case SSH_SERVER_FILE_NOT_FOUND:
470 /* fallthrough */
471 case SSH_SERVER_NOT_KNOWN:
472 keymatch = CURLKHMATCH_MISSING;
473 break;
474 default:
475 keymatch = CURLKHMATCH_MISMATCH;
476 break;
477 }
478#endif
479
480 if(func) { /* use callback to determine action */
481 rc = ssh_pki_export_pubkey_base64(pubkey, &found_base64);
482 if(rc != SSH_OK)
483 goto cleanup;
484
485 foundkey.key = found_base64;
486 foundkey.len = strlen(found_base64);
487
488 switch(ssh_key_type(pubkey)) {
489 case SSH_KEYTYPE_RSA:
490 foundkey.keytype = CURLKHTYPE_RSA;
491 break;
492 case SSH_KEYTYPE_RSA1:
493 foundkey.keytype = CURLKHTYPE_RSA1;
494 break;
495 case SSH_KEYTYPE_ECDSA:
496#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
497 case SSH_KEYTYPE_ECDSA_P256:
498 case SSH_KEYTYPE_ECDSA_P384:
499 case SSH_KEYTYPE_ECDSA_P521:
500#endif
501 foundkey.keytype = CURLKHTYPE_ECDSA;
502 break;
503#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,7,0)
504 case SSH_KEYTYPE_ED25519:
505 foundkey.keytype = CURLKHTYPE_ED25519;
506 break;
507#endif
508 case SSH_KEYTYPE_DSS:
509 foundkey.keytype = CURLKHTYPE_DSS;
510 break;
511 default:
512 rc = SSH_ERROR;
513 goto cleanup;
514 }
515
516 Curl_set_in_callback(data, true);
517 rc = func(data, knownkeyp, /* from the knownhosts file */
518 &foundkey, /* from the remote host */
519 keymatch, data->set.ssh_keyfunc_userp);
520 Curl_set_in_callback(data, false);
521
522 switch(rc) {
523 case CURLKHSTAT_FINE_ADD_TO_FILE:
524#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,8,0)
525 rc = ssh_session_update_known_hosts(sshc->ssh_session);
526#else
527 rc = ssh_write_knownhost(sshc->ssh_session);
528#endif
529 if(rc != SSH_OK) {
530 goto cleanup;
531 }
532 break;
533 case CURLKHSTAT_FINE:
534 break;
535 default: /* REJECT/DEFER */
536 rc = SSH_ERROR;
537 goto cleanup;
538 }
539 }
540 else {
541 if(keymatch != CURLKHMATCH_OK) {
542 rc = SSH_ERROR;
543 goto cleanup;
544 }
545 }
546 rc = SSH_OK;
547
548cleanup:
549 if(found_base64) {
550 (free)(found_base64);
551 }
552 if(known_base64) {
553 (free)(known_base64);
554 }
555 if(hash)
556 ssh_clean_pubkey_hash(&hash);
557 ssh_key_free(pubkey);
558#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,9,0)
559 if(knownhostsentry) {
560 ssh_knownhosts_entry_free(knownhostsentry);
561 }
562#endif
563 return rc;
564}
565
566#define MOVE_TO_ERROR_STATE(_r) do { \
567 state(data, SSH_SESSION_DISCONNECT); \
568 sshc->actualcode = _r; \
569 rc = SSH_ERROR; \
570 } while(0)
571
572#define MOVE_TO_SFTP_CLOSE_STATE() do { \
573 state(data, SSH_SFTP_CLOSE); \
574 sshc->actualcode = \
575 sftp_error_to_CURLE(sftp_get_error(sshc->sftp_session)); \
576 rc = SSH_ERROR; \
577 } while(0)
578
579#define MOVE_TO_LAST_AUTH do { \
580 if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) { \
581 rc = SSH_OK; \
582 state(data, SSH_AUTH_PASS_INIT); \
583 } \
584 else { \
585 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED); \
586 } \
587 } while(0)
588
589#define MOVE_TO_TERTIARY_AUTH do { \
590 if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) { \
591 rc = SSH_OK; \
592 state(data, SSH_AUTH_KEY_INIT); \
593 } \
594 else { \
595 MOVE_TO_LAST_AUTH; \
596 } \
597 } while(0)
598
599#define MOVE_TO_SECONDARY_AUTH do { \
600 if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) { \
601 rc = SSH_OK; \
602 state(data, SSH_AUTH_GSSAPI); \
603 } \
604 else { \
605 MOVE_TO_TERTIARY_AUTH; \
606 } \
607 } while(0)
608
609static
610int myssh_auth_interactive(struct connectdata *conn)
611{
612 int rc;
613 struct ssh_conn *sshc = &conn->proto.sshc;
614 int nprompts;
615
616restart:
617 switch(sshc->kbd_state) {
618 case 0:
619 rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL);
620 if(rc == SSH_AUTH_AGAIN)
621 return SSH_AGAIN;
622
623 if(rc != SSH_AUTH_INFO)
624 return SSH_ERROR;
625
626 nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session);
627 if(nprompts != 1)
628 return SSH_ERROR;
629
630 rc = ssh_userauth_kbdint_setanswer(sshc->ssh_session, 0, conn->passwd);
631 if(rc < 0)
632 return SSH_ERROR;
633
634 /* FALLTHROUGH */
635 case 1:
636 sshc->kbd_state = 1;
637
638 rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL);
639 if(rc == SSH_AUTH_AGAIN)
640 return SSH_AGAIN;
641 else if(rc == SSH_AUTH_SUCCESS)
642 rc = SSH_OK;
643 else if(rc == SSH_AUTH_INFO) {
644 nprompts = ssh_userauth_kbdint_getnprompts(sshc->ssh_session);
645 if(nprompts)
646 return SSH_ERROR;
647
648 sshc->kbd_state = 2;
649 goto restart;
650 }
651 else
652 rc = SSH_ERROR;
653 break;
654 case 2:
655 sshc->kbd_state = 2;
656
657 rc = ssh_userauth_kbdint(sshc->ssh_session, NULL, NULL);
658 if(rc == SSH_AUTH_AGAIN)
659 return SSH_AGAIN;
660 else if(rc == SSH_AUTH_SUCCESS)
661 rc = SSH_OK;
662 else
663 rc = SSH_ERROR;
664
665 break;
666 default:
667 return SSH_ERROR;
668 }
669
670 sshc->kbd_state = 0;
671 return rc;
672}
673
674/*
675 * ssh_statemach_act() runs the SSH state machine as far as it can without
676 * blocking and without reaching the end. The data the pointer 'block' points
677 * to will be set to TRUE if the libssh function returns SSH_AGAIN
678 * meaning it wants to be called again when the socket is ready
679 */
680static CURLcode myssh_statemach_act(struct Curl_easy *data, bool *block)
681{
682 CURLcode result = CURLE_OK;
683 struct connectdata *conn = data->conn;
684 struct SSHPROTO *protop = data->req.p.ssh;
685 struct ssh_conn *sshc = &conn->proto.sshc;
686 curl_socket_t sock = conn->sock[FIRSTSOCKET];
687 int rc = SSH_NO_ERROR, err;
688 int seekerr = CURL_SEEKFUNC_OK;
689 const char *err_msg;
690 *block = 0; /* we're not blocking by default */
691
692 do {
693
694 switch(sshc->state) {
695 case SSH_INIT:
696 sshc->secondCreateDirs = 0;
697 sshc->nextstate = SSH_NO_STATE;
698 sshc->actualcode = CURLE_OK;
699
700#if 0
701 ssh_set_log_level(SSH_LOG_PROTOCOL);
702#endif
703
704 /* Set libssh to non-blocking, since everything internally is
705 non-blocking */
706 ssh_set_blocking(sshc->ssh_session, 0);
707
708 state(data, SSH_S_STARTUP);
709 /* FALLTHROUGH */
710
711 case SSH_S_STARTUP:
712 rc = ssh_connect(sshc->ssh_session);
713 if(rc == SSH_AGAIN)
714 break;
715
716 if(rc != SSH_OK) {
717 failf(data, "Failure establishing ssh session");
718 MOVE_TO_ERROR_STATE(CURLE_FAILED_INIT);
719 break;
720 }
721
722 state(data, SSH_HOSTKEY);
723
724 /* FALLTHROUGH */
725 case SSH_HOSTKEY:
726
727 rc = myssh_is_known(data);
728 if(rc != SSH_OK) {
729 MOVE_TO_ERROR_STATE(CURLE_PEER_FAILED_VERIFICATION);
730 break;
731 }
732
733 state(data, SSH_AUTHLIST);
734 /* FALLTHROUGH */
735 case SSH_AUTHLIST:{
736 sshc->authed = FALSE;
737
738 rc = ssh_userauth_none(sshc->ssh_session, NULL);
739 if(rc == SSH_AUTH_AGAIN) {
740 rc = SSH_AGAIN;
741 break;
742 }
743
744 if(rc == SSH_AUTH_SUCCESS) {
745 sshc->authed = TRUE;
746 infof(data, "Authenticated with none");
747 state(data, SSH_AUTH_DONE);
748 break;
749 }
750 else if(rc == SSH_AUTH_ERROR) {
751 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
752 break;
753 }
754
755 sshc->auth_methods = ssh_userauth_list(sshc->ssh_session, NULL);
756 if(sshc->auth_methods & SSH_AUTH_METHOD_PUBLICKEY) {
757 state(data, SSH_AUTH_PKEY_INIT);
758 infof(data, "Authentication using SSH public key file");
759 }
760 else if(sshc->auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) {
761 state(data, SSH_AUTH_GSSAPI);
762 }
763 else if(sshc->auth_methods & SSH_AUTH_METHOD_INTERACTIVE) {
764 state(data, SSH_AUTH_KEY_INIT);
765 }
766 else if(sshc->auth_methods & SSH_AUTH_METHOD_PASSWORD) {
767 state(data, SSH_AUTH_PASS_INIT);
768 }
769 else { /* unsupported authentication method */
770 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
771 break;
772 }
773
774 break;
775 }
776 case SSH_AUTH_PKEY_INIT:
777 if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY)) {
778 MOVE_TO_SECONDARY_AUTH;
779 break;
780 }
781
782 /* Two choices, (1) private key was given on CMD,
783 * (2) use the "default" keys. */
784 if(data->set.str[STRING_SSH_PRIVATE_KEY]) {
785 if(sshc->pubkey && !data->set.ssl.key_passwd) {
786 rc = ssh_userauth_try_publickey(sshc->ssh_session, NULL,
787 sshc->pubkey);
788 if(rc == SSH_AUTH_AGAIN) {
789 rc = SSH_AGAIN;
790 break;
791 }
792
793 if(rc != SSH_OK) {
794 MOVE_TO_SECONDARY_AUTH;
795 break;
796 }
797 }
798
799 rc = ssh_pki_import_privkey_file(data->
800 set.str[STRING_SSH_PRIVATE_KEY],
801 data->set.ssl.key_passwd, NULL,
802 NULL, &sshc->privkey);
803 if(rc != SSH_OK) {
804 failf(data, "Could not load private key file %s",
805 data->set.str[STRING_SSH_PRIVATE_KEY]);
806 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
807 break;
808 }
809
810 state(data, SSH_AUTH_PKEY);
811 break;
812
813 }
814 else {
815 rc = ssh_userauth_publickey_auto(sshc->ssh_session, NULL,
816 data->set.ssl.key_passwd);
817 if(rc == SSH_AUTH_AGAIN) {
818 rc = SSH_AGAIN;
819 break;
820 }
821 if(rc == SSH_AUTH_SUCCESS) {
822 rc = SSH_OK;
823 sshc->authed = TRUE;
824 infof(data, "Completed public key authentication");
825 state(data, SSH_AUTH_DONE);
826 break;
827 }
828
829 MOVE_TO_SECONDARY_AUTH;
830 }
831 break;
832 case SSH_AUTH_PKEY:
833 rc = ssh_userauth_publickey(sshc->ssh_session, NULL, sshc->privkey);
834 if(rc == SSH_AUTH_AGAIN) {
835 rc = SSH_AGAIN;
836 break;
837 }
838
839 if(rc == SSH_AUTH_SUCCESS) {
840 sshc->authed = TRUE;
841 infof(data, "Completed public key authentication");
842 state(data, SSH_AUTH_DONE);
843 break;
844 }
845 else {
846 infof(data, "Failed public key authentication (rc: %d)", rc);
847 MOVE_TO_SECONDARY_AUTH;
848 }
849 break;
850
851 case SSH_AUTH_GSSAPI:
852 if(!(data->set.ssh_auth_types & CURLSSH_AUTH_GSSAPI)) {
853 MOVE_TO_TERTIARY_AUTH;
854 break;
855 }
856
857 rc = ssh_userauth_gssapi(sshc->ssh_session);
858 if(rc == SSH_AUTH_AGAIN) {
859 rc = SSH_AGAIN;
860 break;
861 }
862
863 if(rc == SSH_AUTH_SUCCESS) {
864 rc = SSH_OK;
865 sshc->authed = TRUE;
866 infof(data, "Completed gssapi authentication");
867 state(data, SSH_AUTH_DONE);
868 break;
869 }
870
871 MOVE_TO_TERTIARY_AUTH;
872 break;
873
874 case SSH_AUTH_KEY_INIT:
875 if(data->set.ssh_auth_types & CURLSSH_AUTH_KEYBOARD) {
876 state(data, SSH_AUTH_KEY);
877 }
878 else {
879 MOVE_TO_LAST_AUTH;
880 }
881 break;
882
883 case SSH_AUTH_KEY:
884
885 /* Authentication failed. Continue with keyboard-interactive now. */
886 rc = myssh_auth_interactive(conn);
887 if(rc == SSH_AGAIN) {
888 break;
889 }
890 if(rc == SSH_OK) {
891 sshc->authed = TRUE;
892 infof(data, "completed keyboard interactive authentication");
893 }
894 state(data, SSH_AUTH_DONE);
895 break;
896
897 case SSH_AUTH_PASS_INIT:
898 if(!(data->set.ssh_auth_types & CURLSSH_AUTH_PASSWORD)) {
899 /* Host key authentication is intentionally not implemented */
900 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
901 break;
902 }
903 state(data, SSH_AUTH_PASS);
904 /* FALLTHROUGH */
905
906 case SSH_AUTH_PASS:
907 rc = ssh_userauth_password(sshc->ssh_session, NULL, conn->passwd);
908 if(rc == SSH_AUTH_AGAIN) {
909 rc = SSH_AGAIN;
910 break;
911 }
912
913 if(rc == SSH_AUTH_SUCCESS) {
914 sshc->authed = TRUE;
915 infof(data, "Completed password authentication");
916 state(data, SSH_AUTH_DONE);
917 }
918 else {
919 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
920 }
921 break;
922
923 case SSH_AUTH_DONE:
924 if(!sshc->authed) {
925 failf(data, "Authentication failure");
926 MOVE_TO_ERROR_STATE(CURLE_LOGIN_DENIED);
927 break;
928 }
929
930 /*
931 * At this point we have an authenticated ssh session.
932 */
933 infof(data, "Authentication complete");
934
935 Curl_pgrsTime(data, TIMER_APPCONNECT); /* SSH is connected */
936
937 conn->sockfd = sock;
938 conn->writesockfd = CURL_SOCKET_BAD;
939
940 if(conn->handler->protocol == CURLPROTO_SFTP) {
941 state(data, SSH_SFTP_INIT);
942 break;
943 }
944 infof(data, "SSH CONNECT phase done");
945 state(data, SSH_STOP);
946 break;
947
948 case SSH_SFTP_INIT:
949 ssh_set_blocking(sshc->ssh_session, 1);
950
951 sshc->sftp_session = sftp_new(sshc->ssh_session);
952 if(!sshc->sftp_session) {
953 failf(data, "Failure initializing sftp session: %s",
954 ssh_get_error(sshc->ssh_session));
955 MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
956 break;
957 }
958
959 rc = sftp_init(sshc->sftp_session);
960 if(rc != SSH_OK) {
961 failf(data, "Failure initializing sftp session: %s",
962 ssh_get_error(sshc->ssh_session));
963 MOVE_TO_ERROR_STATE(sftp_error_to_CURLE(SSH_FX_FAILURE));
964 break;
965 }
966 state(data, SSH_SFTP_REALPATH);
967 /* FALLTHROUGH */
968 case SSH_SFTP_REALPATH:
969 /*
970 * Get the "home" directory
971 */
972 sshc->homedir = sftp_canonicalize_path(sshc->sftp_session, ".");
973 if(!sshc->homedir) {
974 MOVE_TO_ERROR_STATE(CURLE_COULDNT_CONNECT);
975 break;
976 }
977 data->state.most_recent_ftp_entrypath = sshc->homedir;
978
979 /* This is the last step in the SFTP connect phase. Do note that while
980 we get the homedir here, we get the "workingpath" in the DO action
981 since the homedir will remain the same between request but the
982 working path will not. */
983 DEBUGF(infof(data, "SSH CONNECT phase done"));
984 state(data, SSH_STOP);
985 break;
986
987 case SSH_SFTP_QUOTE_INIT:
988 result = Curl_getworkingpath(data, sshc->homedir, &protop->path);
989 if(result) {
990 sshc->actualcode = result;
991 state(data, SSH_STOP);
992 break;
993 }
994
995 if(data->set.quote) {
996 infof(data, "Sending quote commands");
997 sshc->quote_item = data->set.quote;
998 state(data, SSH_SFTP_QUOTE);
999 }
1000 else {
1001 state(data, SSH_SFTP_GETINFO);
1002 }
1003 break;
1004
1005 case SSH_SFTP_POSTQUOTE_INIT:
1006 if(data->set.postquote) {
1007 infof(data, "Sending quote commands");
1008 sshc->quote_item = data->set.postquote;
1009 state(data, SSH_SFTP_QUOTE);
1010 }
1011 else {
1012 state(data, SSH_STOP);
1013 }
1014 break;
1015
1016 case SSH_SFTP_QUOTE:
1017 /* Send any quote commands */
1018 sftp_quote(data);
1019 break;
1020
1021 case SSH_SFTP_NEXT_QUOTE:
1022 Curl_safefree(sshc->quote_path1);
1023 Curl_safefree(sshc->quote_path2);
1024
1025 sshc->quote_item = sshc->quote_item->next;
1026
1027 if(sshc->quote_item) {
1028 state(data, SSH_SFTP_QUOTE);
1029 }
1030 else {
1031 if(sshc->nextstate != SSH_NO_STATE) {
1032 state(data, sshc->nextstate);
1033 sshc->nextstate = SSH_NO_STATE;
1034 }
1035 else {
1036 state(data, SSH_SFTP_GETINFO);
1037 }
1038 }
1039 break;
1040
1041 case SSH_SFTP_QUOTE_STAT:
1042 sftp_quote_stat(data);
1043 break;
1044
1045 case SSH_SFTP_QUOTE_SETSTAT:
1046 rc = sftp_setstat(sshc->sftp_session, sshc->quote_path2,
1047 sshc->quote_attrs);
1048 if(rc && !sshc->acceptfail) {
1049 Curl_safefree(sshc->quote_path1);
1050 Curl_safefree(sshc->quote_path2);
1051 failf(data, "Attempt to set SFTP stats failed: %s",
1052 ssh_get_error(sshc->ssh_session));
1053 state(data, SSH_SFTP_CLOSE);
1054 sshc->nextstate = SSH_NO_STATE;
1055 sshc->actualcode = CURLE_QUOTE_ERROR;
1056 /* sshc->actualcode = sftp_error_to_CURLE(err);
1057 * we do not send the actual error; we return
1058 * the error the libssh2 backend is returning */
1059 break;
1060 }
1061 state(data, SSH_SFTP_NEXT_QUOTE);
1062 break;
1063
1064 case SSH_SFTP_QUOTE_SYMLINK:
1065 rc = sftp_symlink(sshc->sftp_session, sshc->quote_path2,
1066 sshc->quote_path1);
1067 if(rc && !sshc->acceptfail) {
1068 Curl_safefree(sshc->quote_path1);
1069 Curl_safefree(sshc->quote_path2);
1070 failf(data, "symlink command failed: %s",
1071 ssh_get_error(sshc->ssh_session));
1072 state(data, SSH_SFTP_CLOSE);
1073 sshc->nextstate = SSH_NO_STATE;
1074 sshc->actualcode = CURLE_QUOTE_ERROR;
1075 break;
1076 }
1077 state(data, SSH_SFTP_NEXT_QUOTE);
1078 break;
1079
1080 case SSH_SFTP_QUOTE_MKDIR:
1081 rc = sftp_mkdir(sshc->sftp_session, sshc->quote_path1,
1082 (mode_t)data->set.new_directory_perms);
1083 if(rc && !sshc->acceptfail) {
1084 Curl_safefree(sshc->quote_path1);
1085 failf(data, "mkdir command failed: %s",
1086 ssh_get_error(sshc->ssh_session));
1087 state(data, SSH_SFTP_CLOSE);
1088 sshc->nextstate = SSH_NO_STATE;
1089 sshc->actualcode = CURLE_QUOTE_ERROR;
1090 break;
1091 }
1092 state(data, SSH_SFTP_NEXT_QUOTE);
1093 break;
1094
1095 case SSH_SFTP_QUOTE_RENAME:
1096 rc = sftp_rename(sshc->sftp_session, sshc->quote_path1,
1097 sshc->quote_path2);
1098 if(rc && !sshc->acceptfail) {
1099 Curl_safefree(sshc->quote_path1);
1100 Curl_safefree(sshc->quote_path2);
1101 failf(data, "rename command failed: %s",
1102 ssh_get_error(sshc->ssh_session));
1103 state(data, SSH_SFTP_CLOSE);
1104 sshc->nextstate = SSH_NO_STATE;
1105 sshc->actualcode = CURLE_QUOTE_ERROR;
1106 break;
1107 }
1108 state(data, SSH_SFTP_NEXT_QUOTE);
1109 break;
1110
1111 case SSH_SFTP_QUOTE_RMDIR:
1112 rc = sftp_rmdir(sshc->sftp_session, sshc->quote_path1);
1113 if(rc && !sshc->acceptfail) {
1114 Curl_safefree(sshc->quote_path1);
1115 failf(data, "rmdir command failed: %s",
1116 ssh_get_error(sshc->ssh_session));
1117 state(data, SSH_SFTP_CLOSE);
1118 sshc->nextstate = SSH_NO_STATE;
1119 sshc->actualcode = CURLE_QUOTE_ERROR;
1120 break;
1121 }
1122 state(data, SSH_SFTP_NEXT_QUOTE);
1123 break;
1124
1125 case SSH_SFTP_QUOTE_UNLINK:
1126 rc = sftp_unlink(sshc->sftp_session, sshc->quote_path1);
1127 if(rc && !sshc->acceptfail) {
1128 Curl_safefree(sshc->quote_path1);
1129 failf(data, "rm command failed: %s",
1130 ssh_get_error(sshc->ssh_session));
1131 state(data, SSH_SFTP_CLOSE);
1132 sshc->nextstate = SSH_NO_STATE;
1133 sshc->actualcode = CURLE_QUOTE_ERROR;
1134 break;
1135 }
1136 state(data, SSH_SFTP_NEXT_QUOTE);
1137 break;
1138
1139 case SSH_SFTP_QUOTE_STATVFS:
1140 {
1141 sftp_statvfs_t statvfs;
1142
1143 statvfs = sftp_statvfs(sshc->sftp_session, sshc->quote_path1);
1144 if(!statvfs && !sshc->acceptfail) {
1145 Curl_safefree(sshc->quote_path1);
1146 failf(data, "statvfs command failed: %s",
1147 ssh_get_error(sshc->ssh_session));
1148 state(data, SSH_SFTP_CLOSE);
1149 sshc->nextstate = SSH_NO_STATE;
1150 sshc->actualcode = CURLE_QUOTE_ERROR;
1151 break;
1152 }
1153 else if(statvfs) {
1154 char *tmp = aprintf("statvfs:\n"
1155 "f_bsize: %llu\n" "f_frsize: %llu\n"
1156 "f_blocks: %llu\n" "f_bfree: %llu\n"
1157 "f_bavail: %llu\n" "f_files: %llu\n"
1158 "f_ffree: %llu\n" "f_favail: %llu\n"
1159 "f_fsid: %llu\n" "f_flag: %llu\n"
1160 "f_namemax: %llu\n",
1161 statvfs->f_bsize, statvfs->f_frsize,
1162 statvfs->f_blocks, statvfs->f_bfree,
1163 statvfs->f_bavail, statvfs->f_files,
1164 statvfs->f_ffree, statvfs->f_favail,
1165 statvfs->f_fsid, statvfs->f_flag,
1166 statvfs->f_namemax);
1167 sftp_statvfs_free(statvfs);
1168
1169 if(!tmp) {
1170 result = CURLE_OUT_OF_MEMORY;
1171 state(data, SSH_SFTP_CLOSE);
1172 sshc->nextstate = SSH_NO_STATE;
1173 break;
1174 }
1175
1176 result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
1177 free(tmp);
1178 if(result) {
1179 state(data, SSH_SFTP_CLOSE);
1180 sshc->nextstate = SSH_NO_STATE;
1181 sshc->actualcode = result;
1182 }
1183 }
1184 state(data, SSH_SFTP_NEXT_QUOTE);
1185 break;
1186 }
1187
1188 case SSH_SFTP_GETINFO:
1189 if(data->set.get_filetime) {
1190 state(data, SSH_SFTP_FILETIME);
1191 }
1192 else {
1193 state(data, SSH_SFTP_TRANS_INIT);
1194 }
1195 break;
1196
1197 case SSH_SFTP_FILETIME:
1198 {
1199 sftp_attributes attrs;
1200
1201 attrs = sftp_stat(sshc->sftp_session, protop->path);
1202 if(attrs) {
1203 data->info.filetime = attrs->mtime;
1204 sftp_attributes_free(attrs);
1205 }
1206
1207 state(data, SSH_SFTP_TRANS_INIT);
1208 break;
1209 }
1210
1211 case SSH_SFTP_TRANS_INIT:
1212 if(data->set.upload)
1213 state(data, SSH_SFTP_UPLOAD_INIT);
1214 else {
1215 if(protop->path[strlen(protop->path)-1] == '/')
1216 state(data, SSH_SFTP_READDIR_INIT);
1217 else
1218 state(data, SSH_SFTP_DOWNLOAD_INIT);
1219 }
1220 break;
1221
1222 case SSH_SFTP_UPLOAD_INIT:
1223 {
1224 int flags;
1225
1226 if(data->state.resume_from) {
1227 sftp_attributes attrs;
1228
1229 if(data->state.resume_from < 0) {
1230 attrs = sftp_stat(sshc->sftp_session, protop->path);
1231 if(attrs) {
1232 curl_off_t size = attrs->size;
1233 if(size < 0) {
1234 failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
1235 MOVE_TO_ERROR_STATE(CURLE_BAD_DOWNLOAD_RESUME);
1236 break;
1237 }
1238 data->state.resume_from = attrs->size;
1239
1240 sftp_attributes_free(attrs);
1241 }
1242 else {
1243 data->state.resume_from = 0;
1244 }
1245 }
1246 }
1247
1248 if(data->set.remote_append)
1249 /* Try to open for append, but create if nonexisting */
1250 flags = O_WRONLY|O_CREAT|O_APPEND;
1251 else if(data->state.resume_from > 0)
1252 /* If we have restart position then open for append */
1253 flags = O_WRONLY|O_APPEND;
1254 else
1255 /* Clear file before writing (normal behavior) */
1256 flags = O_WRONLY|O_CREAT|O_TRUNC;
1257
1258 if(sshc->sftp_file)
1259 sftp_close(sshc->sftp_file);
1260 sshc->sftp_file =
1261 sftp_open(sshc->sftp_session, protop->path,
1262 flags, (mode_t)data->set.new_file_perms);
1263 if(!sshc->sftp_file) {
1264 err = sftp_get_error(sshc->sftp_session);
1265
1266 if(((err == SSH_FX_NO_SUCH_FILE || err == SSH_FX_FAILURE ||
1267 err == SSH_FX_NO_SUCH_PATH)) &&
1268 (data->set.ftp_create_missing_dirs &&
1269 (strlen(protop->path) > 1))) {
1270 /* try to create the path remotely */
1271 rc = 0;
1272 sshc->secondCreateDirs = 1;
1273 state(data, SSH_SFTP_CREATE_DIRS_INIT);
1274 break;
1275 }
1276 else {
1277 MOVE_TO_SFTP_CLOSE_STATE();
1278 break;
1279 }
1280 }
1281
1282 /* If we have a restart point then we need to seek to the correct
1283 position. */
1284 if(data->state.resume_from > 0) {
1285 /* Let's read off the proper amount of bytes from the input. */
1286 if(conn->seek_func) {
1287 Curl_set_in_callback(data, true);
1288 seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
1289 SEEK_SET);
1290 Curl_set_in_callback(data, false);
1291 }
1292
1293 if(seekerr != CURL_SEEKFUNC_OK) {
1294 curl_off_t passed = 0;
1295
1296 if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
1297 failf(data, "Could not seek stream");
1298 return CURLE_FTP_COULDNT_USE_REST;
1299 }
1300 /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
1301 do {
1302 size_t readthisamountnow =
1303 (data->state.resume_from - passed > data->set.buffer_size) ?
1304 (size_t)data->set.buffer_size :
1305 curlx_sotouz(data->state.resume_from - passed);
1306
1307 size_t actuallyread =
1308 data->state.fread_func(data->state.buffer, 1,
1309 readthisamountnow, data->state.in);
1310
1311 passed += actuallyread;
1312 if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
1313 /* this checks for greater-than only to make sure that the
1314 CURL_READFUNC_ABORT return code still aborts */
1315 failf(data, "Failed to read data");
1316 MOVE_TO_ERROR_STATE(CURLE_FTP_COULDNT_USE_REST);
1317 break;
1318 }
1319 } while(passed < data->state.resume_from);
1320 if(rc)
1321 break;
1322 }
1323
1324 /* now, decrease the size of the read */
1325 if(data->state.infilesize > 0) {
1326 data->state.infilesize -= data->state.resume_from;
1327 data->req.size = data->state.infilesize;
1328 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1329 }
1330
1331 rc = sftp_seek64(sshc->sftp_file, data->state.resume_from);
1332 if(rc) {
1333 MOVE_TO_SFTP_CLOSE_STATE();
1334 break;
1335 }
1336 }
1337 if(data->state.infilesize > 0) {
1338 data->req.size = data->state.infilesize;
1339 Curl_pgrsSetUploadSize(data, data->state.infilesize);
1340 }
1341 /* upload data */
1342 Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
1343
1344 /* not set by Curl_setup_transfer to preserve keepon bits */
1345 conn->sockfd = conn->writesockfd;
1346
1347 /* store this original bitmask setup to use later on if we can't
1348 figure out a "real" bitmask */
1349 sshc->orig_waitfor = data->req.keepon;
1350
1351 /* we want to use the _sending_ function even when the socket turns
1352 out readable as the underlying libssh sftp send function will deal
1353 with both accordingly */
1354 conn->cselect_bits = CURL_CSELECT_OUT;
1355
1356 /* since we don't really wait for anything at this point, we want the
1357 state machine to move on as soon as possible so we set a very short
1358 timeout here */
1359 Curl_expire(data, 0, EXPIRE_RUN_NOW);
1360
1361 state(data, SSH_STOP);
1362 break;
1363 }
1364
1365 case SSH_SFTP_CREATE_DIRS_INIT:
1366 if(strlen(protop->path) > 1) {
1367 sshc->slash_pos = protop->path + 1; /* ignore the leading '/' */
1368 state(data, SSH_SFTP_CREATE_DIRS);
1369 }
1370 else {
1371 state(data, SSH_SFTP_UPLOAD_INIT);
1372 }
1373 break;
1374
1375 case SSH_SFTP_CREATE_DIRS:
1376 sshc->slash_pos = strchr(sshc->slash_pos, '/');
1377 if(sshc->slash_pos) {
1378 *sshc->slash_pos = 0;
1379
1380 infof(data, "Creating directory '%s'", protop->path);
1381 state(data, SSH_SFTP_CREATE_DIRS_MKDIR);
1382 break;
1383 }
1384 state(data, SSH_SFTP_UPLOAD_INIT);
1385 break;
1386
1387 case SSH_SFTP_CREATE_DIRS_MKDIR:
1388 /* 'mode' - parameter is preliminary - default to 0644 */
1389 rc = sftp_mkdir(sshc->sftp_session, protop->path,
1390 (mode_t)data->set.new_directory_perms);
1391 *sshc->slash_pos = '/';
1392 ++sshc->slash_pos;
1393 if(rc < 0) {
1394 /*
1395 * Abort if failure wasn't that the dir already exists or the
1396 * permission was denied (creation might succeed further down the
1397 * path) - retry on unspecific FAILURE also
1398 */
1399 err = sftp_get_error(sshc->sftp_session);
1400 if((err != SSH_FX_FILE_ALREADY_EXISTS) &&
1401 (err != SSH_FX_FAILURE) &&
1402 (err != SSH_FX_PERMISSION_DENIED)) {
1403 MOVE_TO_SFTP_CLOSE_STATE();
1404 break;
1405 }
1406 rc = 0; /* clear rc and continue */
1407 }
1408 state(data, SSH_SFTP_CREATE_DIRS);
1409 break;
1410
1411 case SSH_SFTP_READDIR_INIT:
1412 Curl_pgrsSetDownloadSize(data, -1);
1413 if(data->req.no_body) {
1414 state(data, SSH_STOP);
1415 break;
1416 }
1417
1418 /*
1419 * This is a directory that we are trying to get, so produce a directory
1420 * listing
1421 */
1422 sshc->sftp_dir = sftp_opendir(sshc->sftp_session,
1423 protop->path);
1424 if(!sshc->sftp_dir) {
1425 failf(data, "Could not open directory for reading: %s",
1426 ssh_get_error(sshc->ssh_session));
1427 MOVE_TO_SFTP_CLOSE_STATE();
1428 break;
1429 }
1430 state(data, SSH_SFTP_READDIR);
1431 break;
1432
1433 case SSH_SFTP_READDIR:
1434 Curl_dyn_reset(&sshc->readdir_buf);
1435 if(sshc->readdir_attrs)
1436 sftp_attributes_free(sshc->readdir_attrs);
1437
1438 sshc->readdir_attrs = sftp_readdir(sshc->sftp_session, sshc->sftp_dir);
1439 if(sshc->readdir_attrs) {
1440 sshc->readdir_filename = sshc->readdir_attrs->name;
1441 sshc->readdir_longentry = sshc->readdir_attrs->longname;
1442 sshc->readdir_len = strlen(sshc->readdir_filename);
1443
1444 if(data->set.list_only) {
1445 char *tmpLine;
1446
1447 tmpLine = aprintf("%s\n", sshc->readdir_filename);
1448 if(!tmpLine) {
1449 state(data, SSH_SFTP_CLOSE);
1450 sshc->actualcode = CURLE_OUT_OF_MEMORY;
1451 break;
1452 }
1453 result = Curl_client_write(data, CLIENTWRITE_BODY,
1454 tmpLine, sshc->readdir_len + 1);
1455 free(tmpLine);
1456
1457 if(result) {
1458 state(data, SSH_STOP);
1459 break;
1460 }
1461 /* since this counts what we send to the client, we include the
1462 newline in this counter */
1463 data->req.bytecount += sshc->readdir_len + 1;
1464
1465 /* output debug output if that is requested */
1466 Curl_debug(data, CURLINFO_DATA_OUT, (char *)sshc->readdir_filename,
1467 sshc->readdir_len);
1468 }
1469 else {
1470 if(Curl_dyn_add(&sshc->readdir_buf, sshc->readdir_longentry)) {
1471 sshc->actualcode = CURLE_OUT_OF_MEMORY;
1472 state(data, SSH_STOP);
1473 break;
1474 }
1475
1476 if((sshc->readdir_attrs->flags & SSH_FILEXFER_ATTR_PERMISSIONS) &&
1477 ((sshc->readdir_attrs->permissions & SSH_S_IFMT) ==
1478 SSH_S_IFLNK)) {
1479 sshc->readdir_linkPath = aprintf("%s%s", protop->path,
1480 sshc->readdir_filename);
1481
1482 if(!sshc->readdir_linkPath) {
1483 state(data, SSH_SFTP_CLOSE);
1484 sshc->actualcode = CURLE_OUT_OF_MEMORY;
1485 break;
1486 }
1487
1488 state(data, SSH_SFTP_READDIR_LINK);
1489 break;
1490 }
1491 state(data, SSH_SFTP_READDIR_BOTTOM);
1492 break;
1493 }
1494 }
1495 else if(sftp_dir_eof(sshc->sftp_dir)) {
1496 state(data, SSH_SFTP_READDIR_DONE);
1497 break;
1498 }
1499 else {
1500 failf(data, "Could not open remote file for reading: %s",
1501 ssh_get_error(sshc->ssh_session));
1502 MOVE_TO_SFTP_CLOSE_STATE();
1503 break;
1504 }
1505 break;
1506
1507 case SSH_SFTP_READDIR_LINK:
1508 if(sshc->readdir_link_attrs)
1509 sftp_attributes_free(sshc->readdir_link_attrs);
1510
1511 sshc->readdir_link_attrs = sftp_lstat(sshc->sftp_session,
1512 sshc->readdir_linkPath);
1513 if(sshc->readdir_link_attrs == 0) {
1514 failf(data, "Could not read symlink for reading: %s",
1515 ssh_get_error(sshc->ssh_session));
1516 MOVE_TO_SFTP_CLOSE_STATE();
1517 break;
1518 }
1519
1520 if(!sshc->readdir_link_attrs->name) {
1521 sshc->readdir_tmp = sftp_readlink(sshc->sftp_session,
1522 sshc->readdir_linkPath);
1523 if(!sshc->readdir_filename)
1524 sshc->readdir_len = 0;
1525 else
1526 sshc->readdir_len = strlen(sshc->readdir_tmp);
1527 sshc->readdir_longentry = NULL;
1528 sshc->readdir_filename = sshc->readdir_tmp;
1529 }
1530 else {
1531 sshc->readdir_len = strlen(sshc->readdir_link_attrs->name);
1532 sshc->readdir_filename = sshc->readdir_link_attrs->name;
1533 sshc->readdir_longentry = sshc->readdir_link_attrs->longname;
1534 }
1535
1536 Curl_safefree(sshc->readdir_linkPath);
1537
1538 if(Curl_dyn_addf(&sshc->readdir_buf, " -> %s",
1539 sshc->readdir_filename)) {
1540 sshc->actualcode = CURLE_OUT_OF_MEMORY;
1541 break;
1542 }
1543
1544 sftp_attributes_free(sshc->readdir_link_attrs);
1545 sshc->readdir_link_attrs = NULL;
1546 sshc->readdir_filename = NULL;
1547 sshc->readdir_longentry = NULL;
1548
1549 state(data, SSH_SFTP_READDIR_BOTTOM);
1550 /* FALLTHROUGH */
1551 case SSH_SFTP_READDIR_BOTTOM:
1552 if(Curl_dyn_addn(&sshc->readdir_buf, "\n", 1))
1553 result = CURLE_OUT_OF_MEMORY;
1554 else
1555 result = Curl_client_write(data, CLIENTWRITE_BODY,
1556 Curl_dyn_ptr(&sshc->readdir_buf),
1557 Curl_dyn_len(&sshc->readdir_buf));
1558
1559 if(!result) {
1560 /* output debug output if that is requested */
1561 Curl_debug(data, CURLINFO_DATA_OUT, Curl_dyn_ptr(&sshc->readdir_buf),
1562 Curl_dyn_len(&sshc->readdir_buf));
1563 data->req.bytecount += Curl_dyn_len(&sshc->readdir_buf);
1564 }
1565 ssh_string_free_char(sshc->readdir_tmp);
1566 sshc->readdir_tmp = NULL;
1567
1568 if(result) {
1569 state(data, SSH_STOP);
1570 }
1571 else
1572 state(data, SSH_SFTP_READDIR);
1573 break;
1574
1575 case SSH_SFTP_READDIR_DONE:
1576 sftp_closedir(sshc->sftp_dir);
1577 sshc->sftp_dir = NULL;
1578
1579 /* no data to transfer */
1580 Curl_setup_transfer(data, -1, -1, FALSE, -1);
1581 state(data, SSH_STOP);
1582 break;
1583
1584 case SSH_SFTP_DOWNLOAD_INIT:
1585 /*
1586 * Work on getting the specified file
1587 */
1588 if(sshc->sftp_file)
1589 sftp_close(sshc->sftp_file);
1590
1591 sshc->sftp_file = sftp_open(sshc->sftp_session, protop->path,
1592 O_RDONLY, (mode_t)data->set.new_file_perms);
1593 if(!sshc->sftp_file) {
1594 failf(data, "Could not open remote file for reading: %s",
1595 ssh_get_error(sshc->ssh_session));
1596
1597 MOVE_TO_SFTP_CLOSE_STATE();
1598 break;
1599 }
1600
1601 state(data, SSH_SFTP_DOWNLOAD_STAT);
1602 break;
1603
1604 case SSH_SFTP_DOWNLOAD_STAT:
1605 {
1606 sftp_attributes attrs;
1607 curl_off_t size;
1608
1609 attrs = sftp_fstat(sshc->sftp_file);
1610 if(!attrs ||
1611 !(attrs->flags & SSH_FILEXFER_ATTR_SIZE) ||
1612 (attrs->size == 0)) {
1613 /*
1614 * sftp_fstat didn't return an error, so maybe the server
1615 * just doesn't support stat()
1616 * OR the server doesn't return a file size with a stat()
1617 * OR file size is 0
1618 */
1619 data->req.size = -1;
1620 data->req.maxdownload = -1;
1621 Curl_pgrsSetDownloadSize(data, -1);
1622 size = 0;
1623 }
1624 else {
1625 size = attrs->size;
1626
1627 sftp_attributes_free(attrs);
1628
1629 if(size < 0) {
1630 failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
1631 return CURLE_BAD_DOWNLOAD_RESUME;
1632 }
1633 if(data->state.use_range) {
1634 curl_off_t from, to;
1635 char *ptr;
1636 char *ptr2;
1637 CURLofft to_t;
1638 CURLofft from_t;
1639
1640 from_t = curlx_strtoofft(data->state.range, &ptr, 10, &from);
1641 if(from_t == CURL_OFFT_FLOW) {
1642 return CURLE_RANGE_ERROR;
1643 }
1644 while(*ptr && (ISBLANK(*ptr) || (*ptr == '-')))
1645 ptr++;
1646 to_t = curlx_strtoofft(ptr, &ptr2, 10, &to);
1647 if(to_t == CURL_OFFT_FLOW) {
1648 return CURLE_RANGE_ERROR;
1649 }
1650 if((to_t == CURL_OFFT_INVAL) /* no "to" value given */
1651 || (to >= size)) {
1652 to = size - 1;
1653 }
1654 if(from_t) {
1655 /* from is relative to end of file */
1656 from = size - to;
1657 to = size - 1;
1658 }
1659 if(from > size) {
1660 failf(data, "Offset (%"
1661 CURL_FORMAT_CURL_OFF_T ") was beyond file size (%"
1662 CURL_FORMAT_CURL_OFF_T ")", from, size);
1663 return CURLE_BAD_DOWNLOAD_RESUME;
1664 }
1665 if(from > to) {
1666 from = to;
1667 size = 0;
1668 }
1669 else {
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_setup_transfer(data, -1, -1, FALSE, -1);
1724 infof(data, "File already completely downloaded");
1725 state(data, SSH_STOP);
1726 break;
1727 }
1728 Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1);
1729
1730 /* not set by Curl_setup_transfer 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 conn->cselect_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->set.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_setup_transfer(data, -1, data->req.size, FALSE, FIRSTSOCKET);
1853
1854 /* not set by Curl_setup_transfer 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 conn->cselect_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_setup_transfer(data, FIRSTSOCKET, bytecount, FALSE, -1);
1897
1898 /* not set by Curl_setup_transfer 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 conn->cselect_bits = CURL_CSELECT_IN;
1905
1906 state(data, SSH_STOP);
1907 break;
1908 }
1909 case SSH_SCP_DONE:
1910 if(data->set.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 explicitly mark it as closed with the memdebug macro. This libssh
1959 bug is fixed in 0.10.0. */
1960 fake_sclose(conn->sock[FIRSTSOCKET]);
1961 conn->sock[FIRSTSOCKET] = CURL_SOCKET_BAD;
1962 }
1963
1964 SSH_STRING_FREE_CHAR(sshc->homedir);
1965 data->state.most_recent_ftp_entrypath = NULL;
1966
1967 state(data, SSH_SESSION_FREE);
1968 /* FALLTHROUGH */
1969 case SSH_SESSION_FREE:
1970 if(sshc->ssh_session) {
1971 ssh_free(sshc->ssh_session);
1972 sshc->ssh_session = NULL;
1973 }
1974
1975 /* worst-case scenario cleanup */
1976
1977 DEBUGASSERT(sshc->ssh_session == NULL);
1978 DEBUGASSERT(sshc->scp_session == NULL);
1979
1980 if(sshc->readdir_tmp) {
1981 ssh_string_free_char(sshc->readdir_tmp);
1982 sshc->readdir_tmp = NULL;
1983 }
1984
1985 if(sshc->quote_attrs)
1986 sftp_attributes_free(sshc->quote_attrs);
1987
1988 if(sshc->readdir_attrs)
1989 sftp_attributes_free(sshc->readdir_attrs);
1990
1991 if(sshc->readdir_link_attrs)
1992 sftp_attributes_free(sshc->readdir_link_attrs);
1993
1994 if(sshc->privkey)
1995 ssh_key_free(sshc->privkey);
1996 if(sshc->pubkey)
1997 ssh_key_free(sshc->pubkey);
1998
1999 Curl_safefree(sshc->rsa_pub);
2000 Curl_safefree(sshc->rsa);
2001 Curl_safefree(sshc->quote_path1);
2002 Curl_safefree(sshc->quote_path2);
2003 Curl_dyn_free(&sshc->readdir_buf);
2004 Curl_safefree(sshc->readdir_linkPath);
2005 SSH_STRING_FREE_CHAR(sshc->homedir);
2006
2007 /* the code we are about to return */
2008 result = sshc->actualcode;
2009
2010 memset(sshc, 0, sizeof(struct ssh_conn));
2011
2012 connclose(conn, "SSH session free");
2013 sshc->state = SSH_SESSION_FREE; /* current */
2014 sshc->nextstate = SSH_NO_STATE;
2015 state(data, SSH_STOP);
2016 break;
2017
2018 case SSH_QUIT:
2019 /* fallthrough, just stop! */
2020 default:
2021 /* internal error */
2022 sshc->nextstate = SSH_NO_STATE;
2023 state(data, SSH_STOP);
2024 break;
2025
2026 }
2027 } while(!rc && (sshc->state != SSH_STOP));
2028
2029
2030 if(rc == SSH_AGAIN) {
2031 /* we would block, we need to wait for the socket to be ready (in the
2032 right direction too)! */
2033 *block = TRUE;
2034 }
2035
2036 return result;
2037}
2038
2039
2040/* called by the multi interface to figure out what socket(s) to wait for and
2041 for what actions in the DO_DONE, PERFORM and WAITPERFORM states */
2042static int myssh_getsock(struct Curl_easy *data,
2043 struct connectdata *conn,
2044 curl_socket_t *sock)
2045{
2046 int bitmap = GETSOCK_BLANK;
2047 (void)data;
2048 sock[0] = conn->sock[FIRSTSOCKET];
2049
2050 if(conn->waitfor & KEEP_RECV)
2051 bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
2052
2053 if(conn->waitfor & KEEP_SEND)
2054 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
2055
2056 if(!conn->waitfor)
2057 bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
2058
2059 return bitmap;
2060}
2061
2062static void myssh_block2waitfor(struct connectdata *conn, bool block)
2063{
2064 struct ssh_conn *sshc = &conn->proto.sshc;
2065
2066 /* If it didn't block, or nothing was returned by ssh_get_poll_flags
2067 * have the original set */
2068 conn->waitfor = sshc->orig_waitfor;
2069
2070 if(block) {
2071 int dir = ssh_get_poll_flags(sshc->ssh_session);
2072 if(dir & SSH_READ_PENDING) {
2073 /* translate the libssh define bits into our own bit defines */
2074 conn->waitfor = KEEP_RECV;
2075 }
2076 else if(dir & SSH_WRITE_PENDING) {
2077 conn->waitfor = KEEP_SEND;
2078 }
2079 }
2080}
2081
2082/* called repeatedly until done from multi.c */
2083static CURLcode myssh_multi_statemach(struct Curl_easy *data,
2084 bool *done)
2085{
2086 struct connectdata *conn = data->conn;
2087 struct ssh_conn *sshc = &conn->proto.sshc;
2088 bool block; /* we store the status and use that to provide a ssh_getsock()
2089 implementation */
2090 CURLcode result = myssh_statemach_act(data, &block);
2091
2092 *done = (sshc->state == SSH_STOP) ? TRUE : FALSE;
2093 myssh_block2waitfor(conn, block);
2094
2095 return result;
2096}
2097
2098static CURLcode myssh_block_statemach(struct Curl_easy *data,
2099 bool disconnect)
2100{
2101 struct connectdata *conn = data->conn;
2102 struct ssh_conn *sshc = &conn->proto.sshc;
2103 CURLcode result = CURLE_OK;
2104
2105 while((sshc->state != SSH_STOP) && !result) {
2106 bool block;
2107 timediff_t left = 1000;
2108 struct curltime now = Curl_now();
2109
2110 result = myssh_statemach_act(data, &block);
2111 if(result)
2112 break;
2113
2114 if(!disconnect) {
2115 if(Curl_pgrsUpdate(data))
2116 return CURLE_ABORTED_BY_CALLBACK;
2117
2118 result = Curl_speedcheck(data, now);
2119 if(result)
2120 break;
2121
2122 left = Curl_timeleft(data, NULL, FALSE);
2123 if(left < 0) {
2124 failf(data, "Operation timed out");
2125 return CURLE_OPERATION_TIMEDOUT;
2126 }
2127 }
2128
2129 if(block) {
2130 curl_socket_t fd_read = conn->sock[FIRSTSOCKET];
2131 /* wait for the socket to become ready */
2132 (void) Curl_socket_check(fd_read, CURL_SOCKET_BAD,
2133 CURL_SOCKET_BAD, left > 1000 ? 1000 : left);
2134 }
2135
2136 }
2137
2138 return result;
2139}
2140
2141/*
2142 * SSH setup connection
2143 */
2144static CURLcode myssh_setup_connection(struct Curl_easy *data,
2145 struct connectdata *conn)
2146{
2147 struct SSHPROTO *ssh;
2148 struct ssh_conn *sshc = &conn->proto.sshc;
2149
2150 data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
2151 if(!ssh)
2152 return CURLE_OUT_OF_MEMORY;
2153 Curl_dyn_init(&sshc->readdir_buf, PATH_MAX * 2);
2154
2155 return CURLE_OK;
2156}
2157
2158static Curl_recv scp_recv, sftp_recv;
2159static Curl_send scp_send, sftp_send;
2160
2161/*
2162 * Curl_ssh_connect() gets called from Curl_protocol_connect() to allow us to
2163 * do protocol-specific actions at connect-time.
2164 */
2165static CURLcode myssh_connect(struct Curl_easy *data, bool *done)
2166{
2167 struct ssh_conn *ssh;
2168 CURLcode result;
2169 struct connectdata *conn = data->conn;
2170 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2171 int rc;
2172
2173 /* initialize per-handle data if not already */
2174 if(!data->req.p.ssh)
2175 myssh_setup_connection(data, conn);
2176
2177 /* We default to persistent connections. We set this already in this connect
2178 function to make the re-use checks properly be able to check this bit. */
2179 connkeep(conn, "SSH default");
2180
2181 if(conn->handler->protocol & CURLPROTO_SCP) {
2182 conn->recv[FIRSTSOCKET] = scp_recv;
2183 conn->send[FIRSTSOCKET] = scp_send;
2184 }
2185 else {
2186 conn->recv[FIRSTSOCKET] = sftp_recv;
2187 conn->send[FIRSTSOCKET] = sftp_send;
2188 }
2189
2190 ssh = &conn->proto.sshc;
2191
2192 ssh->ssh_session = ssh_new();
2193 if(!ssh->ssh_session) {
2194 failf(data, "Failure initialising ssh session");
2195 return CURLE_FAILED_INIT;
2196 }
2197
2198 rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_HOST, conn->host.name);
2199 if(rc != SSH_OK) {
2200 failf(data, "Could not set remote host");
2201 return CURLE_FAILED_INIT;
2202 }
2203
2204 rc = ssh_options_parse_config(ssh->ssh_session, NULL);
2205 if(rc != SSH_OK) {
2206 infof(data, "Could not parse SSH configuration files");
2207 /* ignore */
2208 }
2209
2210 rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_FD, &sock);
2211 if(rc != SSH_OK) {
2212 failf(data, "Could not set socket");
2213 return CURLE_FAILED_INIT;
2214 }
2215
2216 if(conn->user && conn->user[0] != '\0') {
2217 infof(data, "User: %s", conn->user);
2218 rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_USER, conn->user);
2219 if(rc != SSH_OK) {
2220 failf(data, "Could not set user");
2221 return CURLE_FAILED_INIT;
2222 }
2223 }
2224
2225 if(data->set.str[STRING_SSH_KNOWNHOSTS]) {
2226 infof(data, "Known hosts: %s", data->set.str[STRING_SSH_KNOWNHOSTS]);
2227 rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_KNOWNHOSTS,
2228 data->set.str[STRING_SSH_KNOWNHOSTS]);
2229 if(rc != SSH_OK) {
2230 failf(data, "Could not set known hosts file path");
2231 return CURLE_FAILED_INIT;
2232 }
2233 }
2234
2235 if(conn->remote_port) {
2236 rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_PORT,
2237 &conn->remote_port);
2238 if(rc != SSH_OK) {
2239 failf(data, "Could not set remote port");
2240 return CURLE_FAILED_INIT;
2241 }
2242 }
2243
2244 if(data->set.ssh_compression) {
2245 rc = ssh_options_set(ssh->ssh_session, SSH_OPTIONS_COMPRESSION,
2246 "zlib,[email protected],none");
2247 if(rc != SSH_OK) {
2248 failf(data, "Could not set compression");
2249 return CURLE_FAILED_INIT;
2250 }
2251 }
2252
2253 ssh->privkey = NULL;
2254 ssh->pubkey = NULL;
2255
2256 if(data->set.str[STRING_SSH_PUBLIC_KEY]) {
2257 rc = ssh_pki_import_pubkey_file(data->set.str[STRING_SSH_PUBLIC_KEY],
2258 &ssh->pubkey);
2259 if(rc != SSH_OK) {
2260 failf(data, "Could not load public key file");
2261 return CURLE_FAILED_INIT;
2262 }
2263 }
2264
2265 /* we do not verify here, we do it at the state machine,
2266 * after connection */
2267
2268 state(data, SSH_INIT);
2269
2270 result = myssh_multi_statemach(data, done);
2271
2272 return result;
2273}
2274
2275/* called from multi.c while DOing */
2276static CURLcode scp_doing(struct Curl_easy *data, bool *dophase_done)
2277{
2278 CURLcode result;
2279
2280 result = myssh_multi_statemach(data, dophase_done);
2281
2282 if(*dophase_done) {
2283 DEBUGF(infof(data, "DO phase is complete"));
2284 }
2285 return result;
2286}
2287
2288/*
2289 ***********************************************************************
2290 *
2291 * scp_perform()
2292 *
2293 * This is the actual DO function for SCP. Get a file according to
2294 * the options previously setup.
2295 */
2296
2297static
2298CURLcode scp_perform(struct Curl_easy *data,
2299 bool *connected, bool *dophase_done)
2300{
2301 CURLcode result = CURLE_OK;
2302
2303 DEBUGF(infof(data, "DO phase starts"));
2304
2305 *dophase_done = FALSE; /* not done yet */
2306
2307 /* start the first command in the DO phase */
2308 state(data, SSH_SCP_TRANS_INIT);
2309
2310 result = myssh_multi_statemach(data, dophase_done);
2311
2312 *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
2313
2314 if(*dophase_done) {
2315 DEBUGF(infof(data, "DO phase is complete"));
2316 }
2317
2318 return result;
2319}
2320
2321static CURLcode myssh_do_it(struct Curl_easy *data, bool *done)
2322{
2323 CURLcode result;
2324 bool connected = 0;
2325 struct connectdata *conn = data->conn;
2326 struct ssh_conn *sshc = &conn->proto.sshc;
2327
2328 *done = FALSE; /* default to false */
2329
2330 data->req.size = -1; /* make sure this is unknown at this point */
2331
2332 sshc->actualcode = CURLE_OK; /* reset error code */
2333 sshc->secondCreateDirs = 0; /* reset the create dir attempt state
2334 variable */
2335
2336 Curl_pgrsSetUploadCounter(data, 0);
2337 Curl_pgrsSetDownloadCounter(data, 0);
2338 Curl_pgrsSetUploadSize(data, -1);
2339 Curl_pgrsSetDownloadSize(data, -1);
2340
2341 if(conn->handler->protocol & CURLPROTO_SCP)
2342 result = scp_perform(data, &connected, done);
2343 else
2344 result = sftp_perform(data, &connected, done);
2345
2346 return result;
2347}
2348
2349/* BLOCKING, but the function is using the state machine so the only reason
2350 this is still blocking is that the multi interface code has no support for
2351 disconnecting operations that takes a while */
2352static CURLcode scp_disconnect(struct Curl_easy *data,
2353 struct connectdata *conn,
2354 bool dead_connection)
2355{
2356 CURLcode result = CURLE_OK;
2357 struct ssh_conn *ssh = &conn->proto.sshc;
2358 (void) dead_connection;
2359
2360 if(ssh->ssh_session) {
2361 /* only if there's a session still around to use! */
2362
2363 state(data, SSH_SESSION_DISCONNECT);
2364
2365 result = myssh_block_statemach(data, TRUE);
2366 }
2367
2368 return result;
2369}
2370
2371/* generic done function for both SCP and SFTP called from their specific
2372 done functions */
2373static CURLcode myssh_done(struct Curl_easy *data, CURLcode status)
2374{
2375 CURLcode result = CURLE_OK;
2376 struct SSHPROTO *protop = data->req.p.ssh;
2377
2378 if(!status) {
2379 /* run the state-machine */
2380 result = myssh_block_statemach(data, FALSE);
2381 }
2382 else
2383 result = status;
2384
2385 if(protop)
2386 Curl_safefree(protop->path);
2387 if(Curl_pgrsDone(data))
2388 return CURLE_ABORTED_BY_CALLBACK;
2389
2390 data->req.keepon = 0; /* clear all bits */
2391 return result;
2392}
2393
2394
2395static CURLcode scp_done(struct Curl_easy *data, CURLcode status,
2396 bool premature)
2397{
2398 (void) premature; /* not used */
2399
2400 if(!status)
2401 state(data, SSH_SCP_DONE);
2402
2403 return myssh_done(data, status);
2404
2405}
2406
2407static ssize_t scp_send(struct Curl_easy *data, int sockindex,
2408 const void *mem, size_t len, CURLcode *err)
2409{
2410 int rc;
2411 struct connectdata *conn = data->conn;
2412 (void) sockindex; /* we only support SCP on the fixed known primary socket */
2413 (void) err;
2414
2415 rc = ssh_scp_write(conn->proto.sshc.scp_session, mem, len);
2416
2417#if 0
2418 /* The following code is misleading, mostly added as wishful thinking
2419 * that libssh at some point will implement non-blocking ssh_scp_write/read.
2420 * Currently rc can only be number of bytes read or SSH_ERROR. */
2421 myssh_block2waitfor(conn, (rc == SSH_AGAIN) ? TRUE : FALSE);
2422
2423 if(rc == SSH_AGAIN) {
2424 *err = CURLE_AGAIN;
2425 return 0;
2426 }
2427 else
2428#endif
2429 if(rc != SSH_OK) {
2430 *err = CURLE_SSH;
2431 return -1;
2432 }
2433
2434 return len;
2435}
2436
2437static ssize_t scp_recv(struct Curl_easy *data, int sockindex,
2438 char *mem, size_t len, CURLcode *err)
2439{
2440 ssize_t nread;
2441 struct connectdata *conn = data->conn;
2442 (void) err;
2443 (void) sockindex; /* we only support SCP on the fixed known primary socket */
2444
2445 /* libssh returns int */
2446 nread = ssh_scp_read(conn->proto.sshc.scp_session, mem, len);
2447
2448#if 0
2449 /* The following code is misleading, mostly added as wishful thinking
2450 * that libssh at some point will implement non-blocking ssh_scp_write/read.
2451 * Currently rc can only be SSH_OK or SSH_ERROR. */
2452
2453 myssh_block2waitfor(conn, (nread == SSH_AGAIN) ? TRUE : FALSE);
2454 if(nread == SSH_AGAIN) {
2455 *err = CURLE_AGAIN;
2456 nread = -1;
2457 }
2458#endif
2459
2460 return nread;
2461}
2462
2463/*
2464 * =============== SFTP ===============
2465 */
2466
2467/*
2468 ***********************************************************************
2469 *
2470 * sftp_perform()
2471 *
2472 * This is the actual DO function for SFTP. Get a file/directory according to
2473 * the options previously setup.
2474 */
2475
2476static
2477CURLcode sftp_perform(struct Curl_easy *data,
2478 bool *connected,
2479 bool *dophase_done)
2480{
2481 CURLcode result = CURLE_OK;
2482
2483 DEBUGF(infof(data, "DO phase starts"));
2484
2485 *dophase_done = FALSE; /* not done yet */
2486
2487 /* start the first command in the DO phase */
2488 state(data, SSH_SFTP_QUOTE_INIT);
2489
2490 /* run the state-machine */
2491 result = myssh_multi_statemach(data, dophase_done);
2492
2493 *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
2494
2495 if(*dophase_done) {
2496 DEBUGF(infof(data, "DO phase is complete"));
2497 }
2498
2499 return result;
2500}
2501
2502/* called from multi.c while DOing */
2503static CURLcode sftp_doing(struct Curl_easy *data,
2504 bool *dophase_done)
2505{
2506 CURLcode result = myssh_multi_statemach(data, dophase_done);
2507 if(*dophase_done) {
2508 DEBUGF(infof(data, "DO phase is complete"));
2509 }
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 */
2516static CURLcode sftp_disconnect(struct Curl_easy *data,
2517 struct connectdata *conn,
2518 bool dead_connection)
2519{
2520 CURLcode result = CURLE_OK;
2521 (void) dead_connection;
2522
2523 DEBUGF(infof(data, "SSH DISCONNECT starts now"));
2524
2525 if(conn->proto.sshc.ssh_session) {
2526 /* only if there's a session still around to use! */
2527 state(data, SSH_SFTP_SHUTDOWN);
2528 result = myssh_block_statemach(data, TRUE);
2529 }
2530
2531 DEBUGF(infof(data, "SSH DISCONNECT is done"));
2532
2533 return result;
2534
2535}
2536
2537static CURLcode sftp_done(struct Curl_easy *data, CURLcode status,
2538 bool premature)
2539{
2540 struct connectdata *conn = data->conn;
2541 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 */
2547 if(!premature && data->set.postquote && !conn->bits.retry)
2548 sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT;
2549 state(data, SSH_SFTP_CLOSE);
2550 }
2551 return myssh_done(data, status);
2552}
2553
2554/* return number of sent bytes */
2555static ssize_t sftp_send(struct Curl_easy *data, int sockindex,
2556 const void *mem, size_t len, CURLcode *err)
2557{
2558 ssize_t nwrite;
2559 struct connectdata *conn = data->conn;
2560 (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 */
2585static ssize_t sftp_recv(struct Curl_easy *data, int sockindex,
2586 char *mem, size_t len, CURLcode *err)
2587{
2588 ssize_t nread;
2589 struct connectdata *conn = data->conn;
2590 (void)sockindex;
2591
2592 DEBUGASSERT(len < CURL_MAX_READ_SIZE);
2593
2594 switch(conn->proto.sshc.sftp_recv_state) {
2595 case 0:
2596 conn->proto.sshc.sftp_file_index =
2597 sftp_async_read_begin(conn->proto.sshc.sftp_file,
2598 (uint32_t)len);
2599 if(conn->proto.sshc.sftp_file_index < 0) {
2600 *err = CURLE_RECV_ERROR;
2601 return -1;
2602 }
2603
2604 /* FALLTHROUGH */
2605 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
2632static void sftp_quote(struct Curl_easy *data)
2633{
2634 const char *cp;
2635 struct connectdata *conn = data->conn;
2636 struct SSHPROTO *protop = data->req.p.ssh;
2637 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;
2662 state(data, SSH_SFTP_CLOSE);
2663 sshc->nextstate = SSH_NO_STATE;
2664 return;
2665 }
2666 Curl_debug(data, CURLINFO_HEADER_OUT, (char *) "PWD\n", 4);
2667 Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp));
2668
2669 /* 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. */
2672 result = Curl_client_write(data, CLIENTWRITE_HEADER, tmp, strlen(tmp));
2673 free(tmp);
2674 if(result) {
2675 state(data, SSH_SFTP_CLOSE);
2676 sshc->nextstate = SSH_NO_STATE;
2677 sshc->actualcode = result;
2678 }
2679 else
2680 state(data, SSH_SFTP_NEXT_QUOTE);
2681 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, ' ');
2689 if(!cp) {
2690 failf(data, "Syntax error in SFTP command. Supply parameter(s)");
2691 state(data, SSH_SFTP_CLOSE);
2692 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");
2707 state(data, SSH_SFTP_CLOSE);
2708 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) ||
2721 strncasecompare(cmd, "chown ", 6) ||
2722 strncasecompare(cmd, "atime ", 6) ||
2723 strncasecompare(cmd, "mtime ", 6)) {
2724 /* 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
2733 failf(data, "Syntax error in chgrp/chmod/chown/atime/mtime: "
2734 "Bad second parameter");
2735 Curl_safefree(sshc->quote_path1);
2736 state(data, SSH_SFTP_CLOSE);
2737 sshc->nextstate = SSH_NO_STATE;
2738 sshc->actualcode = result;
2739 return;
2740 }
2741 sshc->quote_attrs = NULL;
2742 state(data, SSH_SFTP_QUOTE_STAT);
2743 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);
2757 state(data, SSH_SFTP_CLOSE);
2758 sshc->nextstate = SSH_NO_STATE;
2759 sshc->actualcode = result;
2760 return;
2761 }
2762 state(data, SSH_SFTP_QUOTE_SYMLINK);
2763 return;
2764 }
2765 else if(strncasecompare(cmd, "mkdir ", 6)) {
2766 /* create dir */
2767 state(data, SSH_SFTP_QUOTE_MKDIR);
2768 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);
2781 state(data, SSH_SFTP_CLOSE);
2782 sshc->nextstate = SSH_NO_STATE;
2783 sshc->actualcode = result;
2784 return;
2785 }
2786 state(data, SSH_SFTP_QUOTE_RENAME);
2787 return;
2788 }
2789 else if(strncasecompare(cmd, "rmdir ", 6)) {
2790 /* delete dir */
2791 state(data, SSH_SFTP_QUOTE_RMDIR);
2792 return;
2793 }
2794 else if(strncasecompare(cmd, "rm ", 3)) {
2795 state(data, SSH_SFTP_QUOTE_UNLINK);
2796 return;
2797 }
2798#ifdef HAS_STATVFS_SUPPORT
2799 else if(strncasecompare(cmd, "statvfs ", 8)) {
2800 state(data, SSH_SFTP_QUOTE_STATVFS);
2801 return;
2802 }
2803#endif
2804
2805 failf(data, "Unknown SFTP command");
2806 Curl_safefree(sshc->quote_path1);
2807 Curl_safefree(sshc->quote_path2);
2808 state(data, SSH_SFTP_CLOSE);
2809 sshc->nextstate = SSH_NO_STATE;
2810 sshc->actualcode = CURLE_QUOTE_ERROR;
2811}
2812
2813static void sftp_quote_stat(struct Curl_easy *data)
2814{
2815 struct connectdata *conn = data->conn;
2816 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);
2838 if(!sshc->quote_attrs) {
2839 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));
2843 state(data, SSH_SFTP_CLOSE);
2844 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");
2857 state(data, SSH_SFTP_CLOSE);
2858 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");
2872 state(data, SSH_SFTP_CLOSE);
2873 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");
2887 state(data, SSH_SFTP_CLOSE);
2888 sshc->nextstate = SSH_NO_STATE;
2889 sshc->actualcode = CURLE_QUOTE_ERROR;
2890 return;
2891 }
2892 sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_UIDGID;
2893 }
2894 else if(strncasecompare(cmd, "atime", 5) ||
2895 strncasecompare(cmd, "mtime", 5)) {
2896 time_t date = Curl_getdate_capped(sshc->quote_path1);
2897 bool fail = FALSE;
2898 if(date == -1) {
2899 failf(data, "incorrect date format for %.*s", 5, cmd);
2900 fail = TRUE;
2901 }
2902#if SIZEOF_TIME_T > 4
2903 else if(date > 0xffffffff) {
2904 failf(data, "date overflow");
2905 fail = TRUE; /* avoid setting a capped time */
2906 }
2907#endif
2908 if(fail) {
2909 Curl_safefree(sshc->quote_path1);
2910 Curl_safefree(sshc->quote_path2);
2911 state(data, SSH_SFTP_CLOSE);
2912 sshc->nextstate = SSH_NO_STATE;
2913 sshc->actualcode = CURLE_QUOTE_ERROR;
2914 return;
2915 }
2916 if(strncasecompare(cmd, "atime", 5))
2917 sshc->quote_attrs->atime = (uint32_t)date;
2918 else /* mtime */
2919 sshc->quote_attrs->mtime = (uint32_t)date;
2920
2921 sshc->quote_attrs->flags |= SSH_FILEXFER_ATTR_ACMODTIME;
2922 }
2923
2924 /* Now send the completed structure... */
2925 state(data, SSH_SFTP_QUOTE_SETSTAT);
2926 return;
2927}
2928
2929CURLcode Curl_ssh_init(void)
2930{
2931 if(ssh_init()) {
2932 DEBUGF(fprintf(stderr, "Error: libssh_init failed\n"));
2933 return CURLE_FAILED_INIT;
2934 }
2935 return CURLE_OK;
2936}
2937
2938void Curl_ssh_cleanup(void)
2939{
2940 (void)ssh_finalize();
2941}
2942
2943void Curl_ssh_version(char *buffer, size_t buflen)
2944{
2945 (void)msnprintf(buffer, buflen, "libssh/%s", ssh_version(0));
2946}
2947
2948#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