diff options
Diffstat (limited to 'lib/ssh.c')
-rw-r--r-- | lib/ssh.c | 1344 |
1 files changed, 800 insertions, 544 deletions
@@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,14 +22,10 @@ /* #define CURL_LIBSSH2_DEBUG */ -#include "setup.h" +#include "curl_setup.h" #ifdef USE_LIBSSH2 -#include <stdio.h> -#include <string.h> -#include <stdlib.h> -#include <stdarg.h> -#include <ctype.h> + #ifdef HAVE_LIMITS_H # include <limits.h> #endif @@ -37,22 +33,10 @@ #include <libssh2.h> #include <libssh2_sftp.h> -#ifdef HAVE_UNISTD_H -#include <unistd.h> -#endif - #ifdef HAVE_FCNTL_H #include <fcntl.h> #endif -#ifdef HAVE_TIME_H -#include <time.h> -#endif - -#ifndef WIN32 -#ifdef HAVE_SYS_SOCKET_H -#include <sys/socket.h> -#endif #ifdef HAVE_NETINET_IN_H #include <netinet/in.h> #endif @@ -69,7 +53,6 @@ #include <in.h> #include <inet.h> #endif -#endif /* !WIN32 */ #if (defined(NETWARE) && defined(__NOVELL_LIBC__)) #undef in_addr_t @@ -79,8 +62,6 @@ #include <curl/curl.h> #include "urldata.h" #include "sendf.h" -#include "easyif.h" /* for Curl_convert_... prototypes */ - #include "hostip.h" #include "progress.h" #include "transfer.h" @@ -92,7 +73,7 @@ #include "getinfo.h" #include "strequal.h" -#include "sslgen.h" +#include "vtls/vtls.h" #include "connect.h" #include "strerror.h" #include "inet_ntop.h" @@ -101,24 +82,36 @@ #include "strtoofft.h" #include "multiif.h" #include "select.h" - -#define _MPRINTF_REPLACE /* use our functions only */ -#include <curl/mprintf.h> - +#include "warnless.h" +#include "curl_printf.h" #include "curl_memory.h" /* The last #include file should be: */ #include "memdebug.h" +#ifdef WIN32 +# undef PATH_MAX +# define PATH_MAX MAX_PATH +# ifndef R_OK +# define R_OK 4 +# endif +#endif + #ifndef PATH_MAX #define PATH_MAX 1024 /* just an extra precaution since there are systems that have their definition hidden well */ #endif +#define sftp_libssh2_last_error(s) curlx_ultosi(libssh2_sftp_last_error(s)) + +#define sftp_libssh2_realpath(s,p,t,m) \ + libssh2_sftp_symlink_ex((s), (p), curlx_uztoui(strlen(p)), \ + (t), (m), LIBSSH2_SFTP_REALPATH) + /* Local functions: */ -static const char *sftp_libssh2_strerror(unsigned long err); -static LIBSSH2_ALLOC_FUNC(libssh2_malloc); -static LIBSSH2_REALLOC_FUNC(libssh2_realloc); -static LIBSSH2_FREE_FUNC(libssh2_free); +static const char *sftp_libssh2_strerror(int err); +static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc); +static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc); +static LIBSSH2_FREE_FUNC(my_libssh2_free); static CURLcode get_pathname(const char **cpp, char **path); @@ -134,13 +127,13 @@ static CURLcode scp_done(struct connectdata *conn, CURLcode, bool premature); static CURLcode scp_doing(struct connectdata *conn, bool *dophase_done); -static CURLcode scp_disconnect(struct connectdata *conn); +static CURLcode scp_disconnect(struct connectdata *conn, bool dead_connection); static CURLcode sftp_done(struct connectdata *conn, CURLcode, bool premature); static CURLcode sftp_doing(struct connectdata *conn, bool *dophase_done); -static CURLcode sftp_disconnect(struct connectdata *conn); +static CURLcode sftp_disconnect(struct connectdata *conn, bool dead); static CURLcode sftp_perform(struct connectdata *conn, bool *connected, @@ -156,13 +149,15 @@ static int ssh_perform_getsock(const struct connectdata *conn, number of sockets */ int numsocks); +static CURLcode ssh_setup_connection(struct connectdata *conn); + /* * SCP protocol handler. */ const struct Curl_handler Curl_handler_scp = { "SCP", /* scheme */ - ZERO_NULL, /* setup_connection */ + ssh_setup_connection, /* setup_connection */ ssh_do, /* do_it */ scp_done, /* done */ ZERO_NULL, /* do_more */ @@ -171,10 +166,14 @@ const struct Curl_handler Curl_handler_scp = { scp_doing, /* doing */ ssh_getsock, /* proto_getsock */ ssh_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ ssh_perform_getsock, /* perform_getsock */ scp_disconnect, /* disconnect */ + ZERO_NULL, /* readwrite */ PORT_SSH, /* defport */ - PROT_SCP /* protocol */ + CURLPROTO_SCP, /* protocol */ + PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION + | PROTOPT_NOURLQUERY /* flags */ }; @@ -184,7 +183,7 @@ const struct Curl_handler Curl_handler_scp = { const struct Curl_handler Curl_handler_sftp = { "SFTP", /* scheme */ - ZERO_NULL, /* setup_connection */ + ssh_setup_connection, /* setup_connection */ ssh_do, /* do_it */ sftp_done, /* done */ ZERO_NULL, /* do_more */ @@ -193,13 +192,16 @@ const struct Curl_handler Curl_handler_sftp = { sftp_doing, /* doing */ ssh_getsock, /* proto_getsock */ ssh_getsock, /* doing_getsock */ + ZERO_NULL, /* domore_getsock */ ssh_perform_getsock, /* perform_getsock */ sftp_disconnect, /* disconnect */ + ZERO_NULL, /* readwrite */ PORT_SSH, /* defport */ - PROT_SFTP /* protocol */ + CURLPROTO_SFTP, /* protocol */ + PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION + | PROTOPT_NOURLQUERY /* flags */ }; - static void kbd_callback(const char *name, int name_len, const char *instruction, int instruction_len, int num_prompts, @@ -223,7 +225,7 @@ kbd_callback(const char *name, int name_len, const char *instruction, #endif /* CURL_LIBSSH2_DEBUG */ if(num_prompts == 1) { responses[0].text = strdup(conn->passwd); - responses[0].length = (unsigned int)strlen(conn->passwd); + responses[0].length = curlx_uztoui(strlen(conn->passwd)); } (void)prompts; (void)abstract; @@ -301,22 +303,23 @@ static CURLcode libssh2_session_error_to_CURLE(int err) return CURLE_SSH; } -static LIBSSH2_ALLOC_FUNC(libssh2_malloc) +static LIBSSH2_ALLOC_FUNC(my_libssh2_malloc) { (void)abstract; /* arg not used */ return malloc(count); } -static LIBSSH2_REALLOC_FUNC(libssh2_realloc) +static LIBSSH2_REALLOC_FUNC(my_libssh2_realloc) { (void)abstract; /* arg not used */ return realloc(ptr, count); } -static LIBSSH2_FREE_FUNC(libssh2_free) +static LIBSSH2_FREE_FUNC(my_libssh2_free) { (void)abstract; /* arg not used */ - free(ptr); + if(ptr) /* ssh2 agent sometimes call free with null ptr */ + free(ptr); } /* @@ -325,10 +328,12 @@ static LIBSSH2_FREE_FUNC(libssh2_free) /* This is the ONLY way to change SSH state! */ static void state(struct connectdata *conn, sshstate nowstate) { + struct ssh_conn *sshc = &conn->proto.sshc; #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) /* for debug purposes */ static const char * const names[] = { "SSH_STOP", + "SSH_INIT", "SSH_S_STARTUP", "SSH_HOSTKEY", "SSH_AUTHLIST", @@ -336,6 +341,9 @@ static void state(struct connectdata *conn, sshstate nowstate) "SSH_AUTH_PKEY", "SSH_AUTH_PASS_INIT", "SSH_AUTH_PASS", + "SSH_AUTH_AGENT_INIT", + "SSH_AUTH_AGENT_LIST", + "SSH_AUTH_AGENT", "SSH_AUTH_HOST_INIT", "SSH_AUTH_HOST", "SSH_AUTH_KEY_INIT", @@ -380,13 +388,10 @@ static void state(struct connectdata *conn, sshstate nowstate) "SSH_SESSION_FREE", "QUIT" }; -#endif - struct ssh_conn *sshc = &conn->proto.sshc; -#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS) if(sshc->state != nowstate) { infof(conn->data, "SFTP %p state change from %s to %s\n", - sshc, names[sshc->state], names[nowstate]); + (void *)sshc, names[sshc->state], names[nowstate]); } #endif @@ -410,19 +415,19 @@ static CURLcode ssh_getworkingpath(struct connectdata *conn, return CURLE_OUT_OF_MEMORY; /* Check for /~/ , indicating relative to the user's home directory */ - if(conn->protocol & PROT_SCP) { + if(conn->handler->protocol & CURLPROTO_SCP) { real_path = malloc(working_path_len+1); if(real_path == NULL) { free(working_path); return CURLE_OUT_OF_MEMORY; } - if((working_path_len > 1) && (working_path[1] == '~')) - /* It is referenced to the home directory, so strip the leading '/' */ - memcpy(real_path, working_path+1, 1 + working_path_len-1); + if((working_path_len > 3) && (!memcmp(working_path, "/~/", 3))) + /* It is referenced to the home directory, so strip the leading '/~/' */ + memcpy(real_path, working_path+3, 4 + working_path_len-3); else memcpy(real_path, working_path, 1 + working_path_len); } - else if(conn->protocol & PROT_SFTP) { + else if(conn->handler->protocol & CURLPROTO_SFTP) { if((working_path_len > 1) && (working_path[1] == '~')) { size_t homelen = strlen(homedir); real_path = malloc(homelen + working_path_len + 1); @@ -497,6 +502,194 @@ static int sshkeycallback(CURL *easy, #endif /* + * libssh2 1.2.8 fixed the problem with 32bit ints used for sockets on win64. + */ +#ifdef HAVE_LIBSSH2_SESSION_HANDSHAKE +#define libssh2_session_startup(x,y) libssh2_session_handshake(x,y) +#endif + +static CURLcode ssh_knownhost(struct connectdata *conn) +{ + CURLcode result = CURLE_OK; + +#ifdef HAVE_LIBSSH2_KNOWNHOST_API + struct SessionHandle *data = conn->data; + + if(data->set.str[STRING_SSH_KNOWNHOSTS]) { + /* we're asked to verify the host against a file */ + struct ssh_conn *sshc = &conn->proto.sshc; + int rc; + int keytype; + size_t keylen; + const char *remotekey = libssh2_session_hostkey(sshc->ssh_session, + &keylen, &keytype); + int keycheck = LIBSSH2_KNOWNHOST_CHECK_FAILURE; + int keybit = 0; + + if(remotekey) { + /* + * A subject to figure out is what host name we need to pass in here. + * What host name does OpenSSH store in its file if an IDN name is + * used? + */ + struct libssh2_knownhost *host; + enum curl_khmatch keymatch; + curl_sshkeycallback func = + data->set.ssh_keyfunc?data->set.ssh_keyfunc:sshkeycallback; + struct curl_khkey knownkey; + struct curl_khkey *knownkeyp = NULL; + struct curl_khkey foundkey; + + keybit = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)? + LIBSSH2_KNOWNHOST_KEY_SSHRSA:LIBSSH2_KNOWNHOST_KEY_SSHDSS; + +#ifdef HAVE_LIBSSH2_KNOWNHOST_CHECKP + keycheck = libssh2_knownhost_checkp(sshc->kh, + conn->host.name, + (conn->remote_port != PORT_SSH)? + conn->remote_port:-1, + remotekey, keylen, + LIBSSH2_KNOWNHOST_TYPE_PLAIN| + LIBSSH2_KNOWNHOST_KEYENC_RAW| + keybit, + &host); +#else + keycheck = libssh2_knownhost_check(sshc->kh, + conn->host.name, + remotekey, keylen, + LIBSSH2_KNOWNHOST_TYPE_PLAIN| + LIBSSH2_KNOWNHOST_KEYENC_RAW| + keybit, + &host); +#endif + + infof(data, "SSH host check: %d, key: %s\n", keycheck, + (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)? + host->key:"<none>"); + + /* setup 'knownkey' */ + if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) { + knownkey.key = host->key; + knownkey.len = 0; + knownkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)? + CURLKHTYPE_RSA : CURLKHTYPE_DSS; + knownkeyp = &knownkey; + } + + /* setup 'foundkey' */ + foundkey.key = remotekey; + foundkey.len = keylen; + foundkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)? + CURLKHTYPE_RSA : CURLKHTYPE_DSS; + + /* + * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the + * curl_khmatch enum are ever modified, we need to introduce a + * translation table here! + */ + keymatch = (enum curl_khmatch)keycheck; + + /* Ask the callback how to behave */ + rc = func(data, knownkeyp, /* from the knownhosts file */ + &foundkey, /* from the remote host */ + keymatch, data->set.ssh_keyfunc_userp); + } + else + /* no remotekey means failure! */ + rc = CURLKHSTAT_REJECT; + + switch(rc) { + default: /* unknown return codes will equal reject */ + /* FALLTHROUGH */ + case CURLKHSTAT_REJECT: + state(conn, SSH_SESSION_FREE); + /* FALLTHROUGH */ + case CURLKHSTAT_DEFER: + /* DEFER means bail out but keep the SSH_HOSTKEY state */ + result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; + break; + case CURLKHSTAT_FINE: + case CURLKHSTAT_FINE_ADD_TO_FILE: + /* proceed */ + if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) { + /* the found host+key didn't match but has been told to be fine + anyway so we add it in memory */ + int addrc = libssh2_knownhost_add(sshc->kh, + conn->host.name, NULL, + remotekey, keylen, + LIBSSH2_KNOWNHOST_TYPE_PLAIN| + LIBSSH2_KNOWNHOST_KEYENC_RAW| + keybit, NULL); + if(addrc) + infof(data, "Warning adding the known host %s failed!\n", + conn->host.name); + else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE) { + /* now we write the entire in-memory list of known hosts to the + known_hosts file */ + int wrc = + libssh2_knownhost_writefile(sshc->kh, + data->set.str[STRING_SSH_KNOWNHOSTS], + LIBSSH2_KNOWNHOST_FILE_OPENSSH); + if(wrc) { + infof(data, "Warning, writing %s failed!\n", + data->set.str[STRING_SSH_KNOWNHOSTS]); + } + } + } + break; + } + } +#else /* HAVE_LIBSSH2_KNOWNHOST_API */ + (void)conn; +#endif + return result; +} + +static CURLcode ssh_check_fingerprint(struct connectdata *conn) +{ + struct ssh_conn *sshc = &conn->proto.sshc; + struct SessionHandle *data = conn->data; + const char *pubkey_md5 = data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]; + char md5buffer[33]; + int i; + + const char *fingerprint = libssh2_hostkey_hash(sshc->ssh_session, + LIBSSH2_HOSTKEY_HASH_MD5); + + if(fingerprint) { + /* The fingerprint points to static storage (!), don't free() it. */ + for(i = 0; i < 16; i++) + snprintf(&md5buffer[i*2], 3, "%02x", (unsigned char) fingerprint[i]); + infof(data, "SSH MD5 fingerprint: %s\n", md5buffer); + } + + /* Before we authenticate we check the hostkey's MD5 fingerprint + * against a known fingerprint, if available. + */ + if(pubkey_md5 && strlen(pubkey_md5) == 32) { + if(!fingerprint || !strequal(md5buffer, pubkey_md5)) { + if(fingerprint) + failf(data, + "Denied establishing ssh session: mismatch md5 fingerprint. " + "Remote %s is not equal to %s", md5buffer, pubkey_md5); + else + failf(data, + "Denied establishing ssh session: md5 fingerprint not available"); + state(conn, SSH_SESSION_FREE); + sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; + return sshc->actualcode; + } + else { + infof(data, "MD5 checksum match!\n"); + /* as we already matched, we skip the check for known hosts */ + return CURLE_OK; + } + } + else + return ssh_knownhost(conn); +} + +/* * ssh_statemach_act() runs the SSH state machine as far as it can without * blocking and without reaching the end. The data the pointer 'block' points * to will be set to TRUE if the libssh2 function returns LIBSSH2_ERROR_EAGAIN @@ -507,14 +700,11 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) { CURLcode result = CURLE_OK; struct SessionHandle *data = conn->data; - struct SSHPROTO *sftp_scp = data->state.proto.ssh; + struct SSHPROTO *sftp_scp = data->req.protop; struct ssh_conn *sshc = &conn->proto.sshc; curl_socket_t sock = conn->sock[FIRSTSOCKET]; -#ifdef CURL_LIBSSH2_DEBUG - const char *fingerprint; -#endif /* CURL_LIBSSH2_DEBUG */ - const char *host_public_key_md5; - int rc = LIBSSH2_ERROR_NONE, i; + char *new_readdir_line; + int rc = LIBSSH2_ERROR_NONE; int err; int seekerr = CURL_SEEKFUNC_OK; *block = 0; /* we're not blocking by default */ @@ -522,12 +712,20 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) do { switch(sshc->state) { - case SSH_S_STARTUP: + case SSH_INIT: sshc->secondCreateDirs = 0; sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_OK; - rc = libssh2_session_startup(sshc->ssh_session, sock); + /* Set libssh2 to non-blocking, since everything internally is + non-blocking */ + libssh2_session_set_blocking(sshc->ssh_session, 0); + + state(conn, SSH_S_STARTUP); + /* fall-through */ + + case SSH_S_STARTUP: + rc = libssh2_session_startup(sshc->ssh_session, (int)sock); if(rc == LIBSSH2_ERROR_EAGAIN) { break; } @@ -538,168 +736,19 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; } - /* Set libssh2 to non-blocking, since everything internally is - non-blocking */ - libssh2_session_set_blocking(sshc->ssh_session, 0); - state(conn, SSH_HOSTKEY); /* fall-through */ case SSH_HOSTKEY: - -#ifdef CURL_LIBSSH2_DEBUG /* * Before we authenticate we should check the hostkey's fingerprint * against our known hosts. How that is handled (reading from file, - * whatever) is up to us. As for know not much is implemented, besides - * showing how to get the fingerprint. + * whatever) is up to us. */ - fingerprint = libssh2_hostkey_hash(sshc->ssh_session, - LIBSSH2_HOSTKEY_HASH_MD5); - - /* The fingerprint points to static storage (!), don't free() it. */ - infof(data, "Fingerprint: "); - for (rc = 0; rc < 16; rc++) { - infof(data, "%02X ", (unsigned char) fingerprint[rc]); - } - infof(data, "\n"); -#endif /* CURL_LIBSSH2_DEBUG */ - - /* Before we authenticate we check the hostkey's MD5 fingerprint - * against a known fingerprint, if available. This implementation pulls - * it from the curl option. - */ - if(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5] && - strlen(data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]) == 32) { - char buf[33]; - host_public_key_md5 = libssh2_hostkey_hash(sshc->ssh_session, - LIBSSH2_HOSTKEY_HASH_MD5); - for (i = 0; i < 16; i++) - snprintf(&buf[i*2], 3, "%02x", - (unsigned char) host_public_key_md5[i]); - if(!strequal(buf, data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5])) { - failf(data, - "Denied establishing ssh session: mismatch md5 fingerprint. " - "Remote %s is not equal to %s", - buf, data->set.str[STRING_SSH_HOST_PUBLIC_KEY_MD5]); - state(conn, SSH_SESSION_FREE); - sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; - break; - } - } - -#ifdef HAVE_LIBSSH2_KNOWNHOST_API - if(data->set.str[STRING_SSH_KNOWNHOSTS]) { - /* we're asked to verify the host against a file */ - int keytype; - size_t keylen; - const char *remotekey = libssh2_session_hostkey(sshc->ssh_session, - &keylen, &keytype); - int keycheck; - int keybit; - - if(remotekey) { - /* - * A subject to figure out is what host name we need to pass in here. - * What host name does OpenSSH store in its file if an IDN name is - * used? - */ - struct libssh2_knownhost *host; - enum curl_khmatch keymatch; - curl_sshkeycallback func = - data->set.ssh_keyfunc?data->set.ssh_keyfunc:sshkeycallback; - struct curl_khkey knownkey; - struct curl_khkey *knownkeyp = NULL; - struct curl_khkey foundkey; - - keybit = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)? - LIBSSH2_KNOWNHOST_KEY_SSHRSA:LIBSSH2_KNOWNHOST_KEY_SSHDSS; - - keycheck = libssh2_knownhost_check(sshc->kh, - conn->host.name, - remotekey, keylen, - LIBSSH2_KNOWNHOST_TYPE_PLAIN| - LIBSSH2_KNOWNHOST_KEYENC_RAW| - keybit, - &host); - - infof(data, "SSH host check: %d, key: %s\n", keycheck, - (keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)? - host->key:"<none>"); - - /* setup 'knownkey' */ - if(keycheck <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH) { - knownkey.key = host->key; - knownkey.len = 0; - knownkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)? - CURLKHTYPE_RSA : CURLKHTYPE_DSS; - knownkeyp = &knownkey; - } - - /* setup 'foundkey' */ - foundkey.key = remotekey; - foundkey.len = keylen; - foundkey.keytype = (keytype == LIBSSH2_HOSTKEY_TYPE_RSA)? - CURLKHTYPE_RSA : CURLKHTYPE_DSS; - - /* - * if any of the LIBSSH2_KNOWNHOST_CHECK_* defines and the - * curl_khmatch enum are ever modified, we need to introduce a - * translation table here! - */ - keymatch = (enum curl_khmatch)keycheck; - - /* Ask the callback how to behave */ - rc = func(data, knownkeyp, /* from the knownhosts file */ - &foundkey, /* from the remote host */ - keymatch, data->set.ssh_keyfunc_userp); - } - else - /* no remotekey means failure! */ - rc = CURLKHSTAT_REJECT; - - switch(rc) { - default: /* unknown return codes will equal reject */ - case CURLKHSTAT_REJECT: - state(conn, SSH_SESSION_FREE); - case CURLKHSTAT_DEFER: - /* DEFER means bail out but keep the SSH_HOSTKEY state */ - result = sshc->actualcode = CURLE_PEER_FAILED_VERIFICATION; - break; - case CURLKHSTAT_FINE: - case CURLKHSTAT_FINE_ADD_TO_FILE: - /* proceed */ - if(keycheck != LIBSSH2_KNOWNHOST_CHECK_MATCH) { - /* the found host+key didn't match but has been told to be fine - anyway so we add it in memory */ - int addrc = libssh2_knownhost_add(sshc->kh, - conn->host.name, NULL, - remotekey, keylen, - LIBSSH2_KNOWNHOST_TYPE_PLAIN| - LIBSSH2_KNOWNHOST_KEYENC_RAW| - keybit, NULL); - if(addrc) - infof(data, "Warning adding the known host %s failed!\n", - conn->host.name); - else if(rc == CURLKHSTAT_FINE_ADD_TO_FILE) { - /* now we write the entire in-memory list of known hosts to the - known_hosts file */ - int wrc = - libssh2_knownhost_writefile(sshc->kh, - data->set.str[STRING_SSH_KNOWNHOSTS], - LIBSSH2_KNOWNHOST_FILE_OPENSSH); - if(wrc) { - infof(data, "Warning, writing %s failed!\n", - data->set.str[STRING_SSH_KNOWNHOSTS]); - } - } - } - break; - } - } -#endif /* HAVE_LIBSSH2_KNOWNHOST_API */ - - state(conn, SSH_AUTHLIST); + result = ssh_check_fingerprint(conn); + if(!result) + state(conn, SSH_AUTHLIST); + /* ssh_check_fingerprint sets state appropriately on error */ break; case SSH_AUTHLIST: @@ -715,10 +764,16 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) */ sshc->authlist = libssh2_userauth_list(sshc->ssh_session, conn->user, - (unsigned int)strlen(conn->user)); + curlx_uztoui(strlen(conn->user))); if(!sshc->authlist) { - if((err = libssh2_session_last_errno(sshc->ssh_session)) == + if(libssh2_userauth_authenticated(sshc->ssh_session)) { + sshc->authed = TRUE; + infof(data, "SSH user accepted with no authentication\n"); + state(conn, SSH_AUTH_DONE); + break; + } + else if((err = libssh2_session_last_errno(sshc->ssh_session)) == LIBSSH2_ERROR_EAGAIN) { rc = LIBSSH2_ERROR_EAGAIN; break; @@ -744,7 +799,8 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) if((data->set.ssh_auth_types & CURLSSH_AUTH_PUBLICKEY) && (strstr(sshc->authlist, "publickey") != NULL)) { - char *home; + char *home = NULL; + bool out_of_memory = FALSE; sshc->rsa_pub = sshc->rsa = NULL; @@ -752,35 +808,56 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) HOME environment variable etc? */ home = curl_getenv("HOME"); - if(data->set.str[STRING_SSH_PUBLIC_KEY]) - sshc->rsa_pub = aprintf("%s", data->set.str[STRING_SSH_PUBLIC_KEY]); - else if(home) - sshc->rsa_pub = aprintf("%s/.ssh/id_dsa.pub", home); - else - /* as a final resort, try current dir! */ - sshc->rsa_pub = strdup("id_dsa.pub"); - - if(sshc->rsa_pub == NULL) { - Curl_safefree(home); - home = NULL; - state(conn, SSH_SESSION_FREE); - sshc->actualcode = CURLE_OUT_OF_MEMORY; - break; + if(data->set.str[STRING_SSH_PRIVATE_KEY]) + sshc->rsa = strdup(data->set.str[STRING_SSH_PRIVATE_KEY]); + else { + /* If no private key file is specified, try some common paths. */ + if(home) { + /* Try ~/.ssh first. */ + sshc->rsa = aprintf("%s/.ssh/id_rsa", home); + if(!sshc->rsa) + out_of_memory = TRUE; + else if(access(sshc->rsa, R_OK) != 0) { + Curl_safefree(sshc->rsa); + sshc->rsa = aprintf("%s/.ssh/id_dsa", home); + if(!sshc->rsa) + out_of_memory = TRUE; + else if(access(sshc->rsa, R_OK) != 0) { + Curl_safefree(sshc->rsa); + } + } + } + if(!out_of_memory && !sshc->rsa) { + /* Nothing found; try the current dir. */ + sshc->rsa = strdup("id_rsa"); + if(sshc->rsa && access(sshc->rsa, R_OK) != 0) { + Curl_safefree(sshc->rsa); + sshc->rsa = strdup("id_dsa"); + if(sshc->rsa && access(sshc->rsa, R_OK) != 0) { + Curl_safefree(sshc->rsa); + /* Out of guesses. Set to the empty string to avoid + * surprising info messages. */ + sshc->rsa = strdup(""); + } + } + } } - if(data->set.str[STRING_SSH_PRIVATE_KEY]) - sshc->rsa = aprintf("%s", data->set.str[STRING_SSH_PRIVATE_KEY]); - else if(home) - sshc->rsa = aprintf("%s/.ssh/id_dsa", home); - else - /* as a final resort, try current dir! */ - sshc->rsa = strdup("id_dsa"); + /* + * Unless the user explicitly specifies a public key file, let + * libssh2 extract the public key from the private key file. + * This is done by simply passing sshc->rsa_pub = NULL. + */ + if(data->set.str[STRING_SSH_PUBLIC_KEY]) { + sshc->rsa_pub = strdup(data->set.str[STRING_SSH_PUBLIC_KEY]); + if(!sshc->rsa_pub) + out_of_memory = TRUE; + } - if(sshc->rsa == NULL) { - Curl_safefree(home); - home = NULL; + if(out_of_memory || sshc->rsa == NULL) { + free(home); + Curl_safefree(sshc->rsa); Curl_safefree(sshc->rsa_pub); - sshc->rsa_pub = NULL; state(conn, SSH_SESSION_FREE); sshc->actualcode = CURLE_OUT_OF_MEMORY; break; @@ -790,11 +867,10 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) if(!sshc->passphrase) sshc->passphrase = ""; - Curl_safefree(home); - home = NULL; + free(home); - infof(data, "Using ssh public key file %s\n", sshc->rsa_pub); - infof(data, "Using ssh private key file %s\n", sshc->rsa); + infof(data, "Using SSH public key file '%s'\n", sshc->rsa_pub); + infof(data, "Using SSH private key file '%s'\n", sshc->rsa); state(conn, SSH_AUTH_PKEY); } @@ -808,8 +884,8 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) */ rc = libssh2_userauth_publickey_fromfile_ex(sshc->ssh_session, conn->user, - (unsigned int) - strlen(conn->user), + curlx_uztoui( + strlen(conn->user)), sshc->rsa_pub, sshc->rsa, sshc->passphrase); if(rc == LIBSSH2_ERROR_EAGAIN) { @@ -817,9 +893,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } Curl_safefree(sshc->rsa_pub); - sshc->rsa_pub = NULL; Curl_safefree(sshc->rsa); - sshc->rsa = NULL; if(rc == 0) { sshc->authed = TRUE; @@ -847,9 +921,9 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) case SSH_AUTH_PASS: rc = libssh2_userauth_password_ex(sshc->ssh_session, conn->user, - (unsigned int)strlen(conn->user), + curlx_uztoui(strlen(conn->user)), conn->passwd, - (unsigned int)strlen(conn->passwd), + curlx_uztoui(strlen(conn->passwd)), NULL); if(rc == LIBSSH2_ERROR_EAGAIN) { break; @@ -870,12 +944,102 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) state(conn, SSH_AUTH_HOST); } else { - state(conn, SSH_AUTH_KEY_INIT); + state(conn, SSH_AUTH_AGENT_INIT); } break; case SSH_AUTH_HOST: - state(conn, SSH_AUTH_KEY_INIT); + state(conn, SSH_AUTH_AGENT_INIT); + break; + + case SSH_AUTH_AGENT_INIT: +#ifdef HAVE_LIBSSH2_AGENT_API + if((data->set.ssh_auth_types & CURLSSH_AUTH_AGENT) + && (strstr(sshc->authlist, "publickey") != NULL)) { + + /* Connect to the ssh-agent */ + /* The agent could be shared by a curl thread i believe + but nothing obvious as keys can be added/removed at any time */ + if(!sshc->ssh_agent) { + sshc->ssh_agent = libssh2_agent_init(sshc->ssh_session); + if(!sshc->ssh_agent) { + infof(data, "Could not create agent object\n"); + + state(conn, SSH_AUTH_KEY_INIT); + break; + } + } + + rc = libssh2_agent_connect(sshc->ssh_agent); + if(rc == LIBSSH2_ERROR_EAGAIN) + break; + if(rc < 0) { + infof(data, "Failure connecting to agent\n"); + state(conn, SSH_AUTH_KEY_INIT); + } + else { + state(conn, SSH_AUTH_AGENT_LIST); + } + } + else +#endif /* HAVE_LIBSSH2_AGENT_API */ + state(conn, SSH_AUTH_KEY_INIT); + break; + + case SSH_AUTH_AGENT_LIST: +#ifdef HAVE_LIBSSH2_AGENT_API + rc = libssh2_agent_list_identities(sshc->ssh_agent); + + if(rc == LIBSSH2_ERROR_EAGAIN) + break; + if(rc < 0) { + infof(data, "Failure requesting identities to agent\n"); + state(conn, SSH_AUTH_KEY_INIT); + } + else { + state(conn, SSH_AUTH_AGENT); + sshc->sshagent_prev_identity = NULL; + } +#endif + break; + + case SSH_AUTH_AGENT: +#ifdef HAVE_LIBSSH2_AGENT_API + /* as prev_identity evolves only after an identity user auth finished we + can safely request it again as long as EAGAIN is returned here or by + libssh2_agent_userauth */ + rc = libssh2_agent_get_identity(sshc->ssh_agent, + &sshc->sshagent_identity, + sshc->sshagent_prev_identity); + if(rc == LIBSSH2_ERROR_EAGAIN) + break; + + if(rc == 0) { + rc = libssh2_agent_userauth(sshc->ssh_agent, conn->user, + sshc->sshagent_identity); + + if(rc < 0) { + if(rc != LIBSSH2_ERROR_EAGAIN) { + /* tried and failed? go to next identity */ + sshc->sshagent_prev_identity = sshc->sshagent_identity; + } + break; + } + } + + if(rc < 0) + infof(data, "Failure requesting identities to agent\n"); + else if(rc == 1) + infof(data, "No identity would match\n"); + + if(rc == LIBSSH2_ERROR_NONE) { + sshc->authed = TRUE; + infof(data, "Agent based authentication successful\n"); + state(conn, SSH_AUTH_DONE); + } + else + state(conn, SSH_AUTH_KEY_INIT); +#endif break; case SSH_AUTH_KEY_INIT: @@ -892,8 +1056,8 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) /* Authentication failed. Continue with keyboard-interactive now. */ rc = libssh2_userauth_keyboard_interactive_ex(sshc->ssh_session, conn->user, - (unsigned int) - strlen(conn->user), + curlx_uztoui( + strlen(conn->user)), &kbd_callback); if(rc == LIBSSH2_ERROR_EAGAIN) { break; @@ -923,7 +1087,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) conn->sockfd = sock; conn->writesockfd = CURL_SOCKET_BAD; - if(conn->protocol == PROT_SFTP) { + if(conn->handler->protocol == CURLPROTO_SFTP) { state(conn, SSH_SFTP_INIT); break; } @@ -963,7 +1127,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) /* * Get the "home" directory */ - rc = libssh2_sftp_realpath(sshc->sftp_session, ".", + rc = sftp_libssh2_realpath(sshc->sftp_session, ".", tempHome, PATH_MAX-1); if(rc == LIBSSH2_ERROR_EAGAIN) { break; @@ -977,10 +1141,11 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) sshc->actualcode = CURLE_OUT_OF_MEMORY; break; } + conn->data->state.most_recent_ftp_entrypath = sshc->homedir; } else { /* Return the error type */ - err = (int)(libssh2_sftp_last_error(sshc->sftp_session)); + err = sftp_libssh2_last_error(sshc->sftp_session); result = sftp_libssh2_error_to_CURLE(err); sshc->actualcode = result?result:CURLE_SSH; DEBUGF(infof(data, "error = %d makes libcurl = %d\n", @@ -1035,28 +1200,57 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) /* * Support some of the "FTP" commands */ - if(curl_strequal("pwd", sshc->quote_item->data)) { + char *cmd = sshc->quote_item->data; + sshc->acceptfail = FALSE; + + /* if a command starts with an asterisk, which a legal SFTP command never + can, the command will be allowed to fail without it causing any + aborts or cancels etc. It will cause libcurl to act as if the command + is successful, whatever the server reponds. */ + + if(cmd[0] == '*') { + cmd++; + sshc->acceptfail = TRUE; + } + + if(curl_strequal("pwd", cmd)) { /* output debug output if that is requested */ + char *tmp = aprintf("257 \"%s\" is current directory.\n", + sftp_scp->path); + if(!tmp) { + result = CURLE_OUT_OF_MEMORY; + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + break; + } if(data->set.verbose) { - char tmp[PATH_MAX+1]; - Curl_debug(data, CURLINFO_HEADER_OUT, (char *)"PWD\n", 4, conn); - snprintf(tmp, PATH_MAX, "257 \"%s\" is current directory.\n", - sftp_scp->path); Curl_debug(data, CURLINFO_HEADER_IN, tmp, strlen(tmp), conn); } - state(conn, SSH_SFTP_NEXT_QUOTE); + /* this sends an FTP-like "header" to the header callback so that the + current directory can be read very similar to how it is read when + using ordinary FTP. */ + result = Curl_client_write(conn, CLIENTWRITE_HEADER, tmp, strlen(tmp)); + free(tmp); + if(result) { + state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; + sshc->actualcode = result; + } + else + state(conn, SSH_SFTP_NEXT_QUOTE); break; } - else if(sshc->quote_item->data) { + else if(cmd) { /* * the arguments following the command must be separated from the * command with a space so we can check for it unconditionally */ - cp = strchr(sshc->quote_item->data, ' '); + cp = strchr(cmd, ' '); if(cp == NULL) { failf(data, "Syntax error in SFTP command. Supply parameter(s)!"); state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; break; } @@ -1072,19 +1266,20 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) else failf(data, "Syntax error: Bad first parameter"); state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; sshc->actualcode = result; break; } /* - * SFTP is a binary protocol, so we don't send text commands to - * the server. Instead, we scan for commands for commands used by + * SFTP is a binary protocol, so we don't send text commands + * to the server. Instead, we scan for commands used by * OpenSSH's sftp program and call the appropriate libssh2 * functions. */ - if(curl_strnequal(sshc->quote_item->data, "chgrp ", 6) || - curl_strnequal(sshc->quote_item->data, "chmod ", 6) || - curl_strnequal(sshc->quote_item->data, "chown ", 6) ) { + if(curl_strnequal(cmd, "chgrp ", 6) || + curl_strnequal(cmd, "chmod ", 6) || + curl_strnequal(cmd, "chown ", 6) ) { /* attribute change */ /* sshc->quote_path1 contains the mode to set */ @@ -1097,8 +1292,8 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) failf(data, "Syntax error in chgrp/chmod/chown: " "Bad second parameter"); Curl_safefree(sshc->quote_path1); - sshc->quote_path1 = NULL; state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; sshc->actualcode = result; break; } @@ -1106,8 +1301,8 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) state(conn, SSH_SFTP_QUOTE_STAT); break; } - else if(curl_strnequal(sshc->quote_item->data, "ln ", 3) || - curl_strnequal(sshc->quote_item->data, "symlink ", 8)) { + else if(curl_strnequal(cmd, "ln ", 3) || + curl_strnequal(cmd, "symlink ", 8)) { /* symbolic linking */ /* sshc->quote_path1 is the source */ /* get the destination */ @@ -1119,20 +1314,20 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) failf(data, "Syntax error in ln/symlink: Bad second parameter"); Curl_safefree(sshc->quote_path1); - sshc->quote_path1 = NULL; state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; sshc->actualcode = result; break; } state(conn, SSH_SFTP_QUOTE_SYMLINK); break; } - else if(curl_strnequal(sshc->quote_item->data, "mkdir ", 6)) { + else if(curl_strnequal(cmd, "mkdir ", 6)) { /* create dir */ state(conn, SSH_SFTP_QUOTE_MKDIR); break; } - else if(curl_strnequal(sshc->quote_item->data, "rename ", 7)) { + else if(curl_strnequal(cmd, "rename ", 7)) { /* rename file */ /* first param is the source path */ /* second param is the dest. path */ @@ -1143,30 +1338,29 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) else failf(data, "Syntax error in rename: Bad second parameter"); Curl_safefree(sshc->quote_path1); - sshc->quote_path1 = NULL; state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; sshc->actualcode = result; break; } state(conn, SSH_SFTP_QUOTE_RENAME); break; } - else if(curl_strnequal(sshc->quote_item->data, "rmdir ", 6)) { + else if(curl_strnequal(cmd, "rmdir ", 6)) { /* delete dir */ state(conn, SSH_SFTP_QUOTE_RMDIR); break; } - else if(curl_strnequal(sshc->quote_item->data, "rm ", 3)) { + else if(curl_strnequal(cmd, "rm ", 3)) { state(conn, SSH_SFTP_QUOTE_UNLINK); break; } failf(data, "Unknown SFTP command"); Curl_safefree(sshc->quote_path1); - sshc->quote_path1 = NULL; Curl_safefree(sshc->quote_path2); - sshc->quote_path2 = NULL; state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; break; } @@ -1177,14 +1371,8 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; case SSH_SFTP_NEXT_QUOTE: - if(sshc->quote_path1) { - Curl_safefree(sshc->quote_path1); - sshc->quote_path1 = NULL; - } - if(sshc->quote_path2) { - Curl_safefree(sshc->quote_path2); - sshc->quote_path2 = NULL; - } + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); sshc->quote_item = sshc->quote_item->next; @@ -1203,73 +1391,85 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; case SSH_SFTP_QUOTE_STAT: - if(!curl_strnequal(sshc->quote_item->data, "chmod", 5)) { + { + char *cmd = sshc->quote_item->data; + sshc->acceptfail = FALSE; + + /* if a command starts with an asterisk, which a legal SFTP command never + can, the command will be allowed to fail without it causing any + aborts or cancels etc. It will cause libcurl to act as if the command + is successful, whatever the server reponds. */ + + if(cmd[0] == '*') { + cmd++; + sshc->acceptfail = TRUE; + } + + if(!curl_strnequal(cmd, "chmod", 5)) { /* Since chown and chgrp only set owner OR group but libssh2 wants to * set them both at once, we need to obtain the current ownership * first. This takes an extra protocol round trip. */ rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2, - (unsigned int)strlen(sshc->quote_path2), + curlx_uztoui(strlen(sshc->quote_path2)), LIBSSH2_SFTP_STAT, &sshc->quote_attrs); if(rc == LIBSSH2_ERROR_EAGAIN) { break; } - else if(rc != 0) { /* get those attributes */ - err = (int)(libssh2_sftp_last_error(sshc->sftp_session)); + else if(rc != 0 && !sshc->acceptfail) { /* get those attributes */ + err = sftp_libssh2_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); - sshc->quote_path1 = NULL; Curl_safefree(sshc->quote_path2); - sshc->quote_path2 = NULL; failf(data, "Attempt to get SFTP stats failed: %s", sftp_libssh2_strerror(err)); state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; break; } } /* Now set the new attributes... */ - if(curl_strnequal(sshc->quote_item->data, "chgrp", 5)) { + if(curl_strnequal(cmd, "chgrp", 5)) { sshc->quote_attrs.gid = strtoul(sshc->quote_path1, NULL, 10); sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID; - if(sshc->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0])) { + if(sshc->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0]) && + !sshc->acceptfail) { Curl_safefree(sshc->quote_path1); - sshc->quote_path1 = NULL; Curl_safefree(sshc->quote_path2); - sshc->quote_path2 = NULL; failf(data, "Syntax error: chgrp gid not a number"); state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; break; } } - else if(curl_strnequal(sshc->quote_item->data, "chmod", 5)) { + else if(curl_strnequal(cmd, "chmod", 5)) { sshc->quote_attrs.permissions = strtoul(sshc->quote_path1, NULL, 8); sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS; /* permissions are octal */ if(sshc->quote_attrs.permissions == 0 && !ISDIGIT(sshc->quote_path1[0])) { Curl_safefree(sshc->quote_path1); - sshc->quote_path1 = NULL; Curl_safefree(sshc->quote_path2); - sshc->quote_path2 = NULL; failf(data, "Syntax error: chmod permissions not a number"); state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; break; } } - else if(curl_strnequal(sshc->quote_item->data, "chown", 5)) { + else if(curl_strnequal(cmd, "chown", 5)) { sshc->quote_attrs.uid = strtoul(sshc->quote_path1, NULL, 10); sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID; - if(sshc->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0])) { + if(sshc->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0]) && + !sshc->acceptfail) { Curl_safefree(sshc->quote_path1); - sshc->quote_path1 = NULL; Curl_safefree(sshc->quote_path2); - sshc->quote_path2 = NULL; failf(data, "Syntax error: chown uid not a number"); state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; break; } @@ -1278,24 +1478,24 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) /* Now send the completed structure... */ state(conn, SSH_SFTP_QUOTE_SETSTAT); break; + } case SSH_SFTP_QUOTE_SETSTAT: rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2, - (unsigned int)strlen(sshc->quote_path2), + curlx_uztoui(strlen(sshc->quote_path2)), LIBSSH2_SFTP_SETSTAT, &sshc->quote_attrs); if(rc == LIBSSH2_ERROR_EAGAIN) { break; } - else if(rc != 0) { - err = (int)(libssh2_sftp_last_error(sshc->sftp_session)); + else if(rc != 0 && !sshc->acceptfail) { + err = sftp_libssh2_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); - sshc->quote_path1 = NULL; Curl_safefree(sshc->quote_path2); - sshc->quote_path2 = NULL; failf(data, "Attempt to set SFTP stats failed: %s", sftp_libssh2_strerror(err)); state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; break; } @@ -1304,22 +1504,21 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) case SSH_SFTP_QUOTE_SYMLINK: rc = libssh2_sftp_symlink_ex(sshc->sftp_session, sshc->quote_path1, - (unsigned int)strlen(sshc->quote_path1), + curlx_uztoui(strlen(sshc->quote_path1)), sshc->quote_path2, - (unsigned int)strlen(sshc->quote_path2), + curlx_uztoui(strlen(sshc->quote_path2)), LIBSSH2_SFTP_SYMLINK); if(rc == LIBSSH2_ERROR_EAGAIN) { break; } - else if(rc != 0) { - err = (int)(libssh2_sftp_last_error(sshc->sftp_session)); + else if(rc != 0 && !sshc->acceptfail) { + err = sftp_libssh2_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); - sshc->quote_path1 = NULL; Curl_safefree(sshc->quote_path2); - sshc->quote_path2 = NULL; failf(data, "symlink command failed: %s", sftp_libssh2_strerror(err)); state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; break; } @@ -1328,17 +1527,17 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) case SSH_SFTP_QUOTE_MKDIR: rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sshc->quote_path1, - (unsigned int)strlen(sshc->quote_path1), - 0755); + curlx_uztoui(strlen(sshc->quote_path1)), + data->set.new_directory_perms); if(rc == LIBSSH2_ERROR_EAGAIN) { break; } - else if(rc != 0) { - err = (int)(libssh2_sftp_last_error(sshc->sftp_session)); + else if(rc != 0 && !sshc->acceptfail) { + err = sftp_libssh2_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); - sshc->quote_path1 = NULL; failf(data, "mkdir command failed: %s", sftp_libssh2_strerror(err)); state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; break; } @@ -1347,23 +1546,23 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) case SSH_SFTP_QUOTE_RENAME: rc = libssh2_sftp_rename_ex(sshc->sftp_session, sshc->quote_path1, - (unsigned int)strlen(sshc->quote_path1), + curlx_uztoui(strlen(sshc->quote_path1)), sshc->quote_path2, - (unsigned int)strlen(sshc->quote_path2), + curlx_uztoui(strlen(sshc->quote_path2)), LIBSSH2_SFTP_RENAME_OVERWRITE | LIBSSH2_SFTP_RENAME_ATOMIC | LIBSSH2_SFTP_RENAME_NATIVE); + if(rc == LIBSSH2_ERROR_EAGAIN) { break; } - else if(rc != 0) { - err = (int)(libssh2_sftp_last_error(sshc->sftp_session)); + else if(rc != 0 && !sshc->acceptfail) { + err = sftp_libssh2_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); - sshc->quote_path1 = NULL; Curl_safefree(sshc->quote_path2); - sshc->quote_path2 = NULL; failf(data, "rename command failed: %s", sftp_libssh2_strerror(err)); state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; break; } @@ -1372,16 +1571,16 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) case SSH_SFTP_QUOTE_RMDIR: rc = libssh2_sftp_rmdir_ex(sshc->sftp_session, sshc->quote_path1, - (unsigned int)strlen(sshc->quote_path1)); + curlx_uztoui(strlen(sshc->quote_path1))); if(rc == LIBSSH2_ERROR_EAGAIN) { break; } - else if(rc != 0) { - err = (int)(libssh2_sftp_last_error(sshc->sftp_session)); + else if(rc != 0 && !sshc->acceptfail) { + err = sftp_libssh2_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); - sshc->quote_path1 = NULL; failf(data, "rmdir command failed: %s", sftp_libssh2_strerror(err)); state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; break; } @@ -1390,16 +1589,16 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) case SSH_SFTP_QUOTE_UNLINK: rc = libssh2_sftp_unlink_ex(sshc->sftp_session, sshc->quote_path1, - (unsigned int)strlen(sshc->quote_path1)); + curlx_uztoui(strlen(sshc->quote_path1))); if(rc == LIBSSH2_ERROR_EAGAIN) { break; } - else if(rc != 0) { - err = (int)(libssh2_sftp_last_error(sshc->sftp_session)); + else if(rc != 0 && !sshc->acceptfail) { + err = sftp_libssh2_last_error(sshc->sftp_session); Curl_safefree(sshc->quote_path1); - sshc->quote_path1 = NULL; failf(data, "rm command failed: %s", sftp_libssh2_strerror(err)); state(conn, SSH_SFTP_CLOSE); + sshc->nextstate = SSH_NO_STATE; sshc->actualcode = CURLE_QUOTE_ERROR; break; } @@ -1410,9 +1609,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) if(data->set.upload) state(conn, SSH_SFTP_UPLOAD_INIT); else { - if(data->set.opt_no_body) - state(conn, SSH_STOP); - else if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/') + if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/') state(conn, SSH_SFTP_READDIR_INIT); else state(conn, SSH_SFTP_DOWNLOAD_INIT); @@ -1431,9 +1628,9 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) if(data->state.resume_from != 0) { LIBSSH2_SFTP_ATTRIBUTES attrs; - if(data->state.resume_from< 0) { + if(data->state.resume_from < 0) { rc = libssh2_sftp_stat_ex(sshc->sftp_session, sftp_scp->path, - (unsigned int)strlen(sftp_scp->path), + curlx_uztoui(strlen(sftp_scp->path)), LIBSSH2_SFTP_STAT, &attrs); if(rc == LIBSSH2_ERROR_EAGAIN) { break; @@ -1444,7 +1641,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) else { curl_off_t size = attrs.filesize; if(size < 0) { - failf(data, "Bad file size (%" FORMAT_OFF_T ")", size); + failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); return CURLE_BAD_DOWNLOAD_RESUME; } data->state.resume_from = attrs.filesize; @@ -1455,7 +1652,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) if(data->set.ftp_append) /* Try to open for append, but create if nonexisting */ flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_CREAT|LIBSSH2_FXF_APPEND; - else if (data->state.resume_from > 0) + else if(data->state.resume_from > 0) /* If we have restart position then open for append */ flags = LIBSSH2_FXF_WRITE|LIBSSH2_FXF_APPEND; else @@ -1464,7 +1661,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) sshc->sftp_handle = libssh2_sftp_open_ex(sshc->sftp_session, sftp_scp->path, - (unsigned int)strlen(sftp_scp->path), + curlx_uztoui(strlen(sftp_scp->path)), flags, data->set.new_file_perms, LIBSSH2_SFTP_OPENFILE); @@ -1477,7 +1674,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) if(LIBSSH2_ERROR_SFTP_PROTOCOL == rc) /* only when there was an SFTP protocol error can we extract the sftp error! */ - err = (int)(libssh2_sftp_last_error(sshc->sftp_session)); + err = sftp_libssh2_last_error(sshc->sftp_session); else err = -1; /* not an sftp error at all */ @@ -1516,7 +1713,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } } - /* If we have restart point then we need to seek to the correct + /* If we have a restart point then we need to seek to the correct position. */ if(data->state.resume_from > 0) { /* Let's read off the proper amount of bytes from the input. */ @@ -1525,7 +1722,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) SEEK_SET); } - if(seekerr != CURL_SEEKFUNC_OK){ + if(seekerr != CURL_SEEKFUNC_OK) { if(seekerr != CURL_SEEKFUNC_CANTSEEK) { failf(data, "Could not seek stream"); @@ -1534,21 +1731,17 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */ else { curl_off_t passed=0; - curl_off_t readthisamountnow; - curl_off_t actuallyread; do { - readthisamountnow = (data->state.resume_from - passed); - - if(readthisamountnow > BUFSIZE) - readthisamountnow = BUFSIZE; + size_t readthisamountnow = + (data->state.resume_from - passed > CURL_OFF_T_C(BUFSIZE)) ? + BUFSIZE : curlx_sotouz(data->state.resume_from - passed); - actuallyread = - (curl_off_t) conn->fread_func(data->state.buffer, 1, - (size_t)readthisamountnow, - conn->fread_in); + size_t actuallyread = + data->set.fread_func(data->state.buffer, 1, readthisamountnow, + data->set.in); passed += actuallyread; - if((actuallyread <= 0) || (actuallyread > readthisamountnow)) { + if((actuallyread == 0) || (actuallyread > readthisamountnow)) { /* this checks for greater-than only to make sure that the CURL_READFUNC_ABORT return code still aborts */ failf(data, "Failed to read data"); @@ -1559,17 +1752,17 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } /* now, decrease the size of the read */ - if(data->set.infilesize>0) { - data->set.infilesize -= data->state.resume_from; - data->req.size = data->set.infilesize; - Curl_pgrsSetUploadSize(data, data->set.infilesize); + if(data->state.infilesize > 0) { + data->state.infilesize -= data->state.resume_from; + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->state.infilesize); } SFTP_SEEK(sshc->sftp_handle, data->state.resume_from); } - if(data->set.infilesize>0) { - data->req.size = data->set.infilesize; - Curl_pgrsSetUploadSize(data, data->set.infilesize); + if(data->state.infilesize > 0) { + data->req.size = data->state.infilesize; + Curl_pgrsSetUploadSize(data, data->state.infilesize); } /* upload data */ Curl_setup_transfer(conn, -1, -1, FALSE, NULL, FIRSTSOCKET, NULL); @@ -1586,6 +1779,16 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) figure out a "real" bitmask */ sshc->orig_waitfor = data->req.keepon; + /* we want to use the _sending_ function even when the socket turns + out readable as the underlying libssh2 sftp send function will deal + with both accordingly */ + conn->cselect_bits = CURL_CSELECT_OUT; + + /* since we don't really wait for anything at this point, we want the + state machine to move on as soon as possible so we set a very short + timeout here */ + Curl_expire(data, 1); + state(conn, SSH_STOP); } break; @@ -1602,7 +1805,8 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; case SSH_SFTP_CREATE_DIRS: - if((sshc->slash_pos = strchr(sshc->slash_pos, '/')) != NULL) { + sshc->slash_pos = strchr(sshc->slash_pos, '/'); + if(sshc->slash_pos) { *sshc->slash_pos = 0; infof(data, "Creating directory '%s'\n", sftp_scp->path); @@ -1617,7 +1821,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) case SSH_SFTP_CREATE_DIRS_MKDIR: /* 'mode' - parameter is preliminary - default to 0644 */ rc = libssh2_sftp_mkdir_ex(sshc->sftp_session, sftp_scp->path, - (unsigned int)strlen(sftp_scp->path), + curlx_uztoui(strlen(sftp_scp->path)), data->set.new_directory_perms); if(rc == LIBSSH2_ERROR_EAGAIN) { break; @@ -1625,17 +1829,16 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) *sshc->slash_pos = '/'; ++sshc->slash_pos; if(rc == -1) { - unsigned int sftp_err = 0; /* * Abort if failure wasn't that the dir already exists or the * permission was denied (creation might succeed further down the * path) - retry on unspecific FAILURE also */ - sftp_err = (unsigned int)(libssh2_sftp_last_error(sshc->sftp_session)); - if((sftp_err != LIBSSH2_FX_FILE_ALREADY_EXISTS) && - (sftp_err != LIBSSH2_FX_FAILURE) && - (sftp_err != LIBSSH2_FX_PERMISSION_DENIED)) { - result = sftp_libssh2_error_to_CURLE(sftp_err); + err = sftp_libssh2_last_error(sshc->sftp_session); + if((err != LIBSSH2_FX_FILE_ALREADY_EXISTS) && + (err != LIBSSH2_FX_FAILURE) && + (err != LIBSSH2_FX_PERMISSION_DENIED)) { + result = sftp_libssh2_error_to_CURLE(err); state(conn, SSH_SFTP_CLOSE); sshc->actualcode = result?result:CURLE_SSH; break; @@ -1645,14 +1848,20 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; case SSH_SFTP_READDIR_INIT: + Curl_pgrsSetDownloadSize(data, -1); + if(data->set.opt_no_body) { + state(conn, SSH_STOP); + break; + } + /* * This is a directory that we are trying to get, so produce a directory * listing */ sshc->sftp_handle = libssh2_sftp_open_ex(sshc->sftp_session, sftp_scp->path, - (unsigned int) - strlen(sftp_scp->path), + curlx_uztoui( + strlen(sftp_scp->path)), 0, 0, LIBSSH2_SFTP_OPENDIR); if(!sshc->sftp_handle) { if(libssh2_session_last_errno(sshc->ssh_session) == @@ -1661,7 +1870,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; } else { - err = (int)(libssh2_sftp_last_error(sshc->sftp_session)); + err = sftp_libssh2_last_error(sshc->sftp_session); failf(data, "Could not open directory for reading: %s", sftp_libssh2_strerror(err)); state(conn, SSH_SFTP_CLOSE); @@ -1677,7 +1886,6 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } if((sshc->readdir_longentry = malloc(PATH_MAX+1)) == NULL) { Curl_safefree(sshc->readdir_filename); - sshc->readdir_filename = NULL; state(conn, SSH_SFTP_CLOSE); sshc->actualcode = CURLE_OUT_OF_MEMORY; break; @@ -1710,7 +1918,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } result = Curl_client_write(conn, CLIENTWRITE_BODY, tmpLine, sshc->readdir_len+1); - Curl_safefree(tmpLine); + free(tmpLine); if(result) { state(conn, SSH_STOP); @@ -1732,9 +1940,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) sshc->readdir_line = calloc(sshc->readdir_totalLen, 1); if(!sshc->readdir_line) { Curl_safefree(sshc->readdir_filename); - sshc->readdir_filename = NULL; Curl_safefree(sshc->readdir_longentry); - sshc->readdir_longentry = NULL; state(conn, SSH_SFTP_CLOSE); sshc->actualcode = CURLE_OUT_OF_MEMORY; break; @@ -1748,9 +1954,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) sshc->readdir_linkPath = malloc(PATH_MAX + 1); if(sshc->readdir_linkPath == NULL) { Curl_safefree(sshc->readdir_filename); - sshc->readdir_filename = NULL; Curl_safefree(sshc->readdir_longentry); - sshc->readdir_longentry = NULL; state(conn, SSH_SFTP_CLOSE); sshc->actualcode = CURLE_OUT_OF_MEMORY; break; @@ -1767,23 +1971,19 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } else if(sshc->readdir_len == 0) { Curl_safefree(sshc->readdir_filename); - sshc->readdir_filename = NULL; Curl_safefree(sshc->readdir_longentry); - sshc->readdir_longentry = NULL; state(conn, SSH_SFTP_READDIR_DONE); break; } else if(sshc->readdir_len <= 0) { - err = (int)(libssh2_sftp_last_error(sshc->sftp_session)); + err = sftp_libssh2_last_error(sshc->sftp_session); result = sftp_libssh2_error_to_CURLE(err); sshc->actualcode = result?result:CURLE_SSH; failf(data, "Could not open remote file for reading: %s :: %d", sftp_libssh2_strerror(err), libssh2_session_last_errno(sshc->ssh_session)); Curl_safefree(sshc->readdir_filename); - sshc->readdir_filename = NULL; Curl_safefree(sshc->readdir_longentry); - sshc->readdir_longentry = NULL; state(conn, SSH_SFTP_CLOSE); break; } @@ -1793,7 +1993,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) sshc->readdir_len = libssh2_sftp_symlink_ex(sshc->sftp_session, sshc->readdir_linkPath, - (unsigned int) strlen(sshc->readdir_linkPath), + curlx_uztoui(strlen(sshc->readdir_linkPath)), sshc->readdir_filename, PATH_MAX, LIBSSH2_SFTP_READLINK); if(sshc->readdir_len == LIBSSH2_ERROR_EAGAIN) { @@ -1801,19 +2001,19 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; } Curl_safefree(sshc->readdir_linkPath); - sshc->readdir_linkPath = NULL; - sshc->readdir_line = realloc(sshc->readdir_line, - sshc->readdir_totalLen + 4 + - sshc->readdir_len); - if(!sshc->readdir_line) { + + /* get room for the filename and extra output */ + sshc->readdir_totalLen += 4 + sshc->readdir_len; + new_readdir_line = realloc(sshc->readdir_line, sshc->readdir_totalLen); + if(!new_readdir_line) { + Curl_safefree(sshc->readdir_line); Curl_safefree(sshc->readdir_filename); - sshc->readdir_filename = NULL; Curl_safefree(sshc->readdir_longentry); - sshc->readdir_longentry = NULL; state(conn, SSH_SFTP_CLOSE); sshc->actualcode = CURLE_OUT_OF_MEMORY; break; } + sshc->readdir_line = new_readdir_line; sshc->readdir_currLen += snprintf(sshc->readdir_line + sshc->readdir_currLen, @@ -1834,7 +2034,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) sshc->readdir_line, sshc->readdir_currLen); - if(result == CURLE_OK) { + if(!result) { /* output debug output if that is requested */ if(data->set.verbose) { @@ -1844,7 +2044,6 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) data->req.bytecount += sshc->readdir_currLen; } Curl_safefree(sshc->readdir_line); - sshc->readdir_line = NULL; if(result) { state(conn, SSH_STOP); } @@ -1860,9 +2059,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } sshc->sftp_handle = NULL; Curl_safefree(sshc->readdir_filename); - sshc->readdir_filename = NULL; Curl_safefree(sshc->readdir_longentry); - sshc->readdir_longentry = NULL; /* no data to transfer */ Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL); @@ -1875,7 +2072,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) */ sshc->sftp_handle = libssh2_sftp_open_ex(sshc->sftp_session, sftp_scp->path, - (unsigned int)strlen(sftp_scp->path), + curlx_uztoui(strlen(sftp_scp->path)), LIBSSH2_FXF_READ, data->set.new_file_perms, LIBSSH2_SFTP_OPENFILE); if(!sshc->sftp_handle) { @@ -1885,7 +2082,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) break; } else { - err = (int)(libssh2_sftp_last_error(sshc->sftp_session)); + err = sftp_libssh2_last_error(sshc->sftp_session); failf(data, "Could not open remote file for reading: %s", sftp_libssh2_strerror(err)); state(conn, SSH_SFTP_CLOSE); @@ -1902,24 +2099,29 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) LIBSSH2_SFTP_ATTRIBUTES attrs; rc = libssh2_sftp_stat_ex(sshc->sftp_session, sftp_scp->path, - (unsigned int)strlen(sftp_scp->path), + curlx_uztoui(strlen(sftp_scp->path)), LIBSSH2_SFTP_STAT, &attrs); if(rc == LIBSSH2_ERROR_EAGAIN) { break; } - else if(rc) { + else if(rc || + !(attrs.flags & LIBSSH2_SFTP_ATTR_SIZE) || + (attrs.filesize == 0)) { /* * libssh2_sftp_open() didn't return an error, so maybe the server * just doesn't support stat() + * OR the server doesn't return a file size with a stat() + * OR file size is 0 */ data->req.size = -1; data->req.maxdownload = -1; + Curl_pgrsSetDownloadSize(data, -1); } else { curl_off_t size = attrs.filesize; if(size < 0) { - failf(data, "Bad file size (%" FORMAT_OFF_T ")", size); + failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size); return CURLE_BAD_DOWNLOAD_RESUME; } if(conn->data->state.use_range) { @@ -1928,7 +2130,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) char *ptr2; from=curlx_strtoofft(conn->data->state.range, &ptr, 0); - while(*ptr && (isspace((int)*ptr) || (*ptr=='-'))) + while(*ptr && (ISSPACE(*ptr) || (*ptr=='-'))) ptr++; to=curlx_strtoofft(ptr, &ptr2, 0); if((ptr == ptr2) /* no "to" value given */ @@ -1941,8 +2143,8 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } if(from >= size) { failf(data, "Offset (%" - FORMAT_OFF_T ") was beyond file size (%" FORMAT_OFF_T ")", - from, attrs.filesize); + CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" + CURL_FORMAT_CURL_OFF_T ")", from, attrs.filesize); return CURLE_BAD_DOWNLOAD_RESUME; } if(from > to) { @@ -1962,21 +2164,22 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) /* We can resume if we can seek to the resume position */ if(data->state.resume_from) { - if(data->state.resume_from< 0) { + if(data->state.resume_from < 0) { /* We're supposed to download the last abs(from) bytes */ if((curl_off_t)attrs.filesize < -data->state.resume_from) { failf(data, "Offset (%" - FORMAT_OFF_T ") was beyond file size (%" FORMAT_OFF_T ")", + CURL_FORMAT_CURL_OFF_T ") was beyond file size (%" + CURL_FORMAT_CURL_OFF_T ")", data->state.resume_from, attrs.filesize); return CURLE_BAD_DOWNLOAD_RESUME; } /* download from where? */ - data->state.resume_from = attrs.filesize - data->state.resume_from; + data->state.resume_from += attrs.filesize; } else { if((curl_off_t)attrs.filesize < data->state.resume_from) { - failf(data, "Offset (%" FORMAT_OFF_T - ") was beyond file size (%" FORMAT_OFF_T ")", + failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T + ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")", data->state.resume_from, attrs.filesize); return CURLE_BAD_DOWNLOAD_RESUME; } @@ -1990,6 +2193,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) SFTP_SEEK(sshc->sftp_handle, data->state.resume_from); } } + /* Setup the actual download */ if(data->req.size == 0) { /* no data to transfer */ @@ -2005,11 +2209,14 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) /* not set by Curl_setup_transfer to preserve keepon bits */ conn->writesockfd = conn->sockfd; - /* FIXME: here should be explained why we need it to start the - * download */ + /* we want to use the _receiving_ function even when the socket turns + out writableable as the underlying libssh2 recv function will deal + with both accordingly */ conn->cselect_bits = CURL_CSELECT_IN; } if(result) { + /* this should never occur; the close state should be entered + at the time the error occurs */ state(conn, SSH_SFTP_CLOSE); sshc->actualcode = result; } @@ -2029,15 +2236,23 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } sshc->sftp_handle = NULL; } - Curl_safefree(sftp_scp->path); - sftp_scp->path = NULL; + if(sftp_scp) + Curl_safefree(sftp_scp->path); DEBUGF(infof(data, "SFTP DONE done\n")); -#if 0 /* PREV */ - state(conn, SSH_SFTP_SHUTDOWN); -#endif - state(conn, SSH_STOP); - result = sshc->actualcode; + + /* Check if nextstate is set and move .nextstate could be POSTQUOTE_INIT + After nextstate is executed, the control should come back to + SSH_SFTP_CLOSE to pass the correct result back */ + if(sshc->nextstate != SSH_NO_STATE && + sshc->nextstate != SSH_SFTP_CLOSE) { + state(conn, sshc->nextstate); + sshc->nextstate = SSH_SFTP_CLOSE; + } + else { + state(conn, SSH_STOP); + result = sshc->actualcode; + } break; case SSH_SFTP_SHUTDOWN: @@ -2067,7 +2282,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } Curl_safefree(sshc->homedir); - sshc->homedir = NULL; + conn->data->state.most_recent_ftp_entrypath = NULL; state(conn, SSH_SESSION_DISCONNECT); break; @@ -2081,7 +2296,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } if(data->set.upload) { - if(data->set.infilesize < 0) { + if(data->state.infilesize < 0) { failf(data, "SCP requires a known file size for upload"); sshc->actualcode = CURLE_UPLOAD_FAILED; state(conn, SSH_SCP_CHANNEL_FREE); @@ -2103,7 +2318,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) */ sshc->ssh_channel = SCP_SEND(sshc->ssh_session, sftp_scp->path, data->set.new_file_perms, - data->set.infilesize); + data->state.infilesize); if(!sshc->ssh_channel) { if(libssh2_session_last_errno(sshc->ssh_session) == LIBSSH2_ERROR_EAGAIN) { @@ -2135,6 +2350,15 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) sshc->actualcode = result; } else { + /* store this original bitmask setup to use later on if we can't + figure out a "real" bitmask */ + sshc->orig_waitfor = data->req.keepon; + + /* we want to use the _sending_ function even when the socket turns + out readable as the underlying libssh2 scp send function will deal + with both accordingly */ + conn->cselect_bits = CURL_CSELECT_OUT; + state(conn, SSH_STOP); } break; @@ -2181,8 +2405,9 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) /* not set by Curl_setup_transfer to preserve keepon bits */ conn->writesockfd = conn->sockfd; - /* FIXME: here should be explained why we need it to start the - * download */ + /* we want to use the _receiving_ function even when the socket turns + out writableable as the underlying libssh2 recv function will deal + with both accordingly */ conn->cselect_bits = CURL_CSELECT_IN; if(result) { @@ -2285,7 +2510,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } Curl_safefree(sshc->homedir); - sshc->homedir = NULL; + conn->data->state.most_recent_ftp_entrypath = NULL; state(conn, SSH_SESSION_FREE); break; @@ -2298,6 +2523,25 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } #endif +#ifdef HAVE_LIBSSH2_AGENT_API + if(sshc->ssh_agent) { + rc = libssh2_agent_disconnect(sshc->ssh_agent); + if(rc == LIBSSH2_ERROR_EAGAIN) { + break; + } + else if(rc < 0) { + infof(data, "Failed to disconnect from libssh2 agent\n"); + } + libssh2_agent_free (sshc->ssh_agent); + sshc->ssh_agent = NULL; + + /* NB: there is no need to free identities, they are part of internal + agent stuff */ + sshc->sshagent_identity = NULL; + sshc->sshagent_prev_identity = NULL; + } +#endif + if(sshc->ssh_session) { rc = libssh2_session_free(sshc->ssh_session); if(rc == LIBSSH2_ERROR_EAGAIN) { @@ -2308,10 +2552,42 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block) } sshc->ssh_session = NULL; } - conn->bits.close = TRUE; + + /* worst-case scenario cleanup */ + + DEBUGASSERT(sshc->ssh_session == NULL); + DEBUGASSERT(sshc->ssh_channel == NULL); + DEBUGASSERT(sshc->sftp_session == NULL); + DEBUGASSERT(sshc->sftp_handle == NULL); +#ifdef HAVE_LIBSSH2_KNOWNHOST_API + DEBUGASSERT(sshc->kh == NULL); +#endif +#ifdef HAVE_LIBSSH2_AGENT_API + DEBUGASSERT(sshc->ssh_agent == NULL); +#endif + + Curl_safefree(sshc->rsa_pub); + Curl_safefree(sshc->rsa); + + Curl_safefree(sshc->quote_path1); + Curl_safefree(sshc->quote_path2); + + Curl_safefree(sshc->homedir); + + Curl_safefree(sshc->readdir_filename); + Curl_safefree(sshc->readdir_longentry); + Curl_safefree(sshc->readdir_line); + Curl_safefree(sshc->readdir_linkPath); + + /* the code we are about to return */ + result = sshc->actualcode; + + memset(sshc, 0, sizeof(struct ssh_conn)); + + connclose(conn, "SSH session free"); + sshc->state = SSH_SESSION_FREE; /* current */ sshc->nextstate = SSH_NO_STATE; state(conn, SSH_STOP); - result = sshc->actualcode; break; case SSH_QUIT: @@ -2394,9 +2670,7 @@ static void ssh_block2waitfor(struct connectdata *conn, bool block) { struct ssh_conn *sshc = &conn->proto.sshc; int dir; - if(!block) - conn->waitfor = 0; - else if((dir = libssh2_session_block_directions(sshc->ssh_session))) { + if(block && (dir = libssh2_session_block_directions(sshc->ssh_session))) { /* translate the libssh2 define bits into our own bit defines */ conn->waitfor = ((dir&LIBSSH2_SESSION_BLOCK_INBOUND)?KEEP_RECV:0) | ((dir&LIBSSH2_SESSION_BLOCK_OUTBOUND)?KEEP_SEND:0); @@ -2408,7 +2682,7 @@ static void ssh_block2waitfor(struct connectdata *conn, bool block) } #else /* no libssh2 directional support so we simply don't know */ -#define ssh_block2waitfor(x,y) +#define ssh_block2waitfor(x,y) Curl_nop_stmt #endif /* called repeatedly until done from multi.c */ @@ -2420,13 +2694,13 @@ static CURLcode ssh_multi_statemach(struct connectdata *conn, bool *done) implementation */ result = ssh_statemach_act(conn, &block); - *done = (bool)(sshc->state == SSH_STOP); + *done = (sshc->state == SSH_STOP) ? TRUE : FALSE; ssh_block2waitfor(conn, block); return result; } -static CURLcode ssh_easy_statemach(struct connectdata *conn, +static CURLcode ssh_block_statemach(struct connectdata *conn, bool duringconnect) { struct ssh_conn *sshc = &conn->proto.sshc; @@ -2438,31 +2712,37 @@ static CURLcode ssh_easy_statemach(struct connectdata *conn, long left; result = ssh_statemach_act(conn, &block); + if(result) + break; if(Curl_pgrsUpdate(conn)) return CURLE_ABORTED_BY_CALLBACK; + else { + struct timeval now = Curl_tvnow(); + result = Curl_speedcheck(data, now); + if(result) + break; + } - left = Curl_timeleft(conn, NULL, duringconnect); + left = Curl_timeleft(data, NULL, duringconnect); if(left < 0) { - failf(data, "Operation timed out\n"); + failf(data, "Operation timed out"); return CURLE_OPERATION_TIMEDOUT; } #ifdef HAVE_LIBSSH2_SESSION_BLOCK_DIRECTION - if((CURLE_OK == result) && block) { + if(!result && block) { int dir = libssh2_session_block_directions(sshc->ssh_session); curl_socket_t sock = conn->sock[FIRSTSOCKET]; curl_socket_t fd_read = CURL_SOCKET_BAD; curl_socket_t fd_write = CURL_SOCKET_BAD; - if (LIBSSH2_SESSION_BLOCK_INBOUND & dir) { + if(LIBSSH2_SESSION_BLOCK_INBOUND & dir) fd_read = sock; - } - if (LIBSSH2_SESSION_BLOCK_OUTBOUND & dir) { + if(LIBSSH2_SESSION_BLOCK_OUTBOUND & dir) fd_write = sock; - } /* wait for the socket to become ready */ Curl_socket_ready(fd_read, fd_write, - (int)(left>1000?1000:left)); /* ignore result */ + left>1000?1000:left); /* ignore result */ } #endif @@ -2474,25 +2754,14 @@ static CURLcode ssh_easy_statemach(struct connectdata *conn, /* * SSH setup and connection */ -static CURLcode ssh_init(struct connectdata *conn) +static CURLcode ssh_setup_connection(struct connectdata *conn) { - struct SessionHandle *data = conn->data; struct SSHPROTO *ssh; - struct ssh_conn *sshc = &conn->proto.sshc; - sshc->actualcode = CURLE_OK; /* reset error code */ - sshc->secondCreateDirs =0; /* reset the create dir attempt state - variable */ - - if(data->state.proto.ssh) - return CURLE_OK; - - ssh = calloc(1, sizeof(struct SSHPROTO)); + conn->data->req.protop = ssh = calloc(1, sizeof(struct SSHPROTO)); if(!ssh) return CURLE_OUT_OF_MEMORY; - data->state.proto.ssh = ssh; - return CURLE_OK; } @@ -2512,22 +2781,19 @@ static CURLcode ssh_connect(struct connectdata *conn, bool *done) CURLcode result; struct SessionHandle *data = conn->data; + /* initialize per-handle data if not already */ + if(!data->req.protop) + ssh_setup_connection(conn); + /* We default to persistent connections. We set this already in this connect function to make the re-use checks properly be able to check this bit. */ - conn->bits.close = FALSE; - - /* If there already is a protocol-specific struct allocated for this - sessionhandle, deal with it */ - Curl_reset_reqproto(conn); + connkeep(conn, "SSH default"); - result = ssh_init(conn); - if(result) - return result; - - if(conn->protocol & PROT_SCP) { + if(conn->handler->protocol & CURLPROTO_SCP) { conn->recv[FIRSTSOCKET] = scp_recv; conn->send[FIRSTSOCKET] = scp_send; - } else { + } + else { conn->recv[FIRSTSOCKET] = sftp_recv; conn->send[FIRSTSOCKET] = sftp_send; } @@ -2543,8 +2809,9 @@ static CURLcode ssh_connect(struct connectdata *conn, bool *done) sock = conn->sock[FIRSTSOCKET]; #endif /* CURL_LIBSSH2_DEBUG */ - ssh->ssh_session = libssh2_session_init_ex(libssh2_malloc, libssh2_free, - libssh2_realloc, conn); + ssh->ssh_session = libssh2_session_init_ex(my_libssh2_malloc, + my_libssh2_free, + my_libssh2_realloc, conn); if(ssh->ssh_session == NULL) { failf(data, "Failure initialising ssh session"); return CURLE_FAILED_INIT; @@ -2563,10 +2830,9 @@ static CURLcode ssh_connect(struct connectdata *conn, bool *done) rc = libssh2_knownhost_readfile(ssh->kh, data->set.str[STRING_SSH_KNOWNHOSTS], LIBSSH2_KNOWNHOST_FILE_OPENSSH); - if(rc) { + if(rc < 0) infof(data, "Failed to read known hosts from %s\n", data->set.str[STRING_SSH_KNOWNHOSTS]); - } } #endif /* HAVE_LIBSSH2_KNOWNHOST_API */ @@ -2575,15 +2841,9 @@ static CURLcode ssh_connect(struct connectdata *conn, bool *done) infof(data, "SSH socket: %d\n", (int)sock); #endif /* CURL_LIBSSH2_DEBUG */ - state(conn, SSH_S_STARTUP); + state(conn, SSH_INIT); - if(data->state.used_interface == Curl_if_multi) - result = ssh_multi_statemach(conn, done); - else { - result = ssh_easy_statemach(conn, TRUE); - if(!result) - *done = TRUE; - } + result = ssh_multi_statemach(conn, done); return result; } @@ -2612,14 +2872,9 @@ CURLcode scp_perform(struct connectdata *conn, state(conn, SSH_SCP_TRANS_INIT); /* run the state-machine */ - if(conn->data->state.used_interface == Curl_if_multi) { - result = ssh_multi_statemach(conn, dophase_done); - } - else { - result = ssh_easy_statemach(conn, FALSE); - *dophase_done = TRUE; /* with the easy interface we are done here */ - } - *connected = conn->bits.tcpconnect; + result = ssh_multi_statemach(conn, dophase_done); + + *connected = conn->bits.tcpconnect[FIRSTSOCKET]; if(*dophase_done) { DEBUGF(infof(conn->data, "DO phase is complete\n")); @@ -2648,56 +2903,49 @@ static CURLcode scp_doing(struct connectdata *conn, static CURLcode ssh_do(struct connectdata *conn, bool *done) { - CURLcode res; + CURLcode result; bool connected = 0; struct SessionHandle *data = conn->data; + struct ssh_conn *sshc = &conn->proto.sshc; *done = FALSE; /* default to false */ - /* - Since connections can be re-used between SessionHandles, this might be a - connection already existing but on a fresh SessionHandle struct so we must - make sure we have a good 'struct SSHPROTO' to play with. For new - connections, the struct SSHPROTO is allocated and setup in the - ssh_connect() function. - */ - Curl_reset_reqproto(conn); - res = ssh_init(conn); - if(res) - return res; - data->req.size = -1; /* make sure this is unknown at this point */ + sshc->actualcode = CURLE_OK; /* reset error code */ + sshc->secondCreateDirs =0; /* reset the create dir attempt state + variable */ + Curl_pgrsSetUploadCounter(data, 0); Curl_pgrsSetDownloadCounter(data, 0); - Curl_pgrsSetUploadSize(data, 0); - Curl_pgrsSetDownloadSize(data, 0); + Curl_pgrsSetUploadSize(data, -1); + Curl_pgrsSetDownloadSize(data, -1); - if(conn->protocol & PROT_SCP) - res = scp_perform(conn, &connected, done); + if(conn->handler->protocol & CURLPROTO_SCP) + result = scp_perform(conn, &connected, done); else - res = sftp_perform(conn, &connected, done); + result = sftp_perform(conn, &connected, done); - return res; + return result; } /* BLOCKING, but the function is using the state machine so the only reason this is still blocking is that the multi interface code has no support for disconnecting operations that takes a while */ -static CURLcode scp_disconnect(struct connectdata *conn) +static CURLcode scp_disconnect(struct connectdata *conn, bool dead_connection) { CURLcode result = CURLE_OK; struct ssh_conn *ssh = &conn->proto.sshc; + (void) dead_connection; - Curl_safefree(conn->data->state.proto.ssh); - conn->data->state.proto.ssh = NULL; + Curl_safefree(conn->data->req.protop); if(ssh->ssh_session) { /* only if there's a session still around to use! */ state(conn, SSH_SESSION_DISCONNECT); - result = ssh_easy_statemach(conn, FALSE); + result = ssh_block_statemach(conn, FALSE); } return result; @@ -2708,9 +2956,9 @@ static CURLcode scp_disconnect(struct connectdata *conn) static CURLcode ssh_done(struct connectdata *conn, CURLcode status) { CURLcode result = CURLE_OK; - struct SSHPROTO *sftp_scp = conn->data->state.proto.ssh; + struct SSHPROTO *sftp_scp = conn->data->req.protop; - if(status == CURLE_OK) { + if(!status) { /* run the state-machine TODO: when the multi interface is used, this _really_ should be using @@ -2718,14 +2966,15 @@ static CURLcode ssh_done(struct connectdata *conn, CURLcode status) non-blocking DONE operations, not in the multi state machine and with Curl_done() invokes on several places in the code! */ - result = ssh_easy_statemach(conn, FALSE); + result = ssh_block_statemach(conn, FALSE); } else result = status; - Curl_safefree(sftp_scp->path); - sftp_scp->path = NULL; - Curl_pgrsDone(conn); + if(sftp_scp) + Curl_safefree(sftp_scp->path); + if(Curl_pgrsDone(conn)) + return CURLE_ABORTED_BY_CALLBACK; conn->data->req.keepon = 0; /* clear all bits */ return result; @@ -2737,7 +2986,7 @@ static CURLcode scp_done(struct connectdata *conn, CURLcode status, { (void)premature; /* not used */ - if(status == CURLE_OK) + if(!status) state(conn, SSH_SCP_DONE); return ssh_done(conn, status); @@ -2761,6 +3010,10 @@ static ssize_t scp_send(struct connectdata *conn, int sockindex, *err = CURLE_AGAIN; nwrite = 0; } + else if(nwrite < LIBSSH2_ERROR_NONE) { + *err = libssh2_session_error_to_CURLE((int)nwrite); + nwrite = -1; + } return nwrite; } @@ -2780,7 +3033,7 @@ static ssize_t scp_recv(struct connectdata *conn, int sockindex, libssh2_channel_read(conn->proto.sshc.ssh_channel, mem, len); ssh_block2waitfor(conn, (nread == LIBSSH2_ERROR_EAGAIN)?TRUE:FALSE); - if (nread == LIBSSH2_ERROR_EAGAIN) { + if(nread == LIBSSH2_ERROR_EAGAIN) { *err = CURLE_AGAIN; nread = -1; } @@ -2816,14 +3069,9 @@ CURLcode sftp_perform(struct connectdata *conn, state(conn, SSH_SFTP_QUOTE_INIT); /* run the state-machine */ - if(conn->data->state.used_interface == Curl_if_multi) { - result = ssh_multi_statemach(conn, dophase_done); - } - else { - result = ssh_easy_statemach(conn, FALSE); - *dophase_done = TRUE; /* with the easy interface we are done here */ - } - *connected = conn->bits.tcpconnect; + result = ssh_multi_statemach(conn, dophase_done); + + *connected = conn->bits.tcpconnect[FIRSTSOCKET]; if(*dophase_done) { DEBUGF(infof(conn->data, "DO phase is complete\n")); @@ -2836,8 +3084,7 @@ CURLcode sftp_perform(struct connectdata *conn, static CURLcode sftp_doing(struct connectdata *conn, bool *dophase_done) { - CURLcode result; - result = ssh_multi_statemach(conn, dophase_done); + CURLcode result = ssh_multi_statemach(conn, dophase_done); if(*dophase_done) { DEBUGF(infof(conn->data, "DO phase is complete\n")); @@ -2848,19 +3095,19 @@ static CURLcode sftp_doing(struct connectdata *conn, /* BLOCKING, but the function is using the state machine so the only reason this is still blocking is that the multi interface code has no support for disconnecting operations that takes a while */ -static CURLcode sftp_disconnect(struct connectdata *conn) +static CURLcode sftp_disconnect(struct connectdata *conn, bool dead_connection) { CURLcode result = CURLE_OK; + (void) dead_connection; DEBUGF(infof(conn->data, "SSH DISCONNECT starts now\n")); - Curl_safefree(conn->data->state.proto.ssh); - conn->data->state.proto.ssh = NULL; + Curl_safefree(conn->data->req.protop); if(conn->proto.sshc.ssh_session) { /* only if there's a session still around to use! */ state(conn, SSH_SFTP_SHUTDOWN); - result = ssh_easy_statemach(conn, FALSE); + result = ssh_block_statemach(conn, FALSE); } DEBUGF(infof(conn->data, "SSH DISCONNECT is done\n")); @@ -2874,12 +3121,13 @@ static CURLcode sftp_done(struct connectdata *conn, CURLcode status, { struct ssh_conn *sshc = &conn->proto.sshc; - if(status == CURLE_OK) { - /* Before we shut down, see if there are any post-quote commands to - send: */ + if(!status) { + /* Post quote commands are executed after the SFTP_CLOSE state to avoid + errors that could happen due to open file handles during POSTQUOTE + operation */ if(!status && !premature && conn->data->set.postquote) { - sshc->nextstate = SSH_SFTP_CLOSE; - state(conn, SSH_SFTP_POSTQUOTE_INIT); + sshc->nextstate = SSH_SFTP_POSTQUOTE_INIT; + state(conn, SSH_SFTP_CLOSE); } else state(conn, SSH_SFTP_CLOSE); @@ -2904,12 +3152,17 @@ static ssize_t sftp_send(struct connectdata *conn, int sockindex, *err = CURLE_AGAIN; nwrite = 0; } + else if(nwrite < LIBSSH2_ERROR_NONE) { + *err = libssh2_session_error_to_CURLE((int)nwrite); + nwrite = -1; + } return nwrite; } /* * Return number of received (decrypted) bytes + * or <0 on error */ static ssize_t sftp_recv(struct connectdata *conn, int sockindex, char *mem, size_t len, CURLcode *err) @@ -2924,6 +3177,10 @@ static ssize_t sftp_recv(struct connectdata *conn, int sockindex, if(nread == LIBSSH2_ERROR_EAGAIN) { *err = CURLE_AGAIN; nread = -1; + + } + else if(nread < 0) { + *err = libssh2_session_error_to_CURLE((int)nread); } return nread; } @@ -2969,7 +3226,7 @@ get_pathname(const char **cpp, char **path) quot = *cp++; /* Search for terminating quote, unescape some chars */ - for (i = j = 0; i <= strlen(cp); i++) { + for(i = j = 0; i <= strlen(cp); i++) { if(cp[i] == quot) { /* Found quote */ i++; (*path)[j] = '\0'; @@ -3010,13 +3267,12 @@ get_pathname(const char **cpp, char **path) return CURLE_OK; fail: - Curl_safefree(*path); - *path = NULL; - return CURLE_QUOTE_ERROR; + Curl_safefree(*path); + return CURLE_QUOTE_ERROR; } -static const char *sftp_libssh2_strerror(unsigned long err) +static const char *sftp_libssh2_strerror(int err) { switch (err) { case LIBSSH2_FX_NO_SUCH_FILE: |