Return to libssh.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / lib / vssh |
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 */