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