VirtualBox

source: vbox/trunk/src/libs/curl-7.64.0/lib/ssh-libssh.c@ 95219

Last change on this file since 95219 was 85671, checked in by vboxsync, 4 years ago

Export out internal curl copy to make it a lot simpler to build VBox (OSE) on Windows. bugref:9814

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

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