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