1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at https://curl.se/docs/copyright.html.
13  *
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * SPDX-License-Identifier: curl
22  *
23  ***************************************************************************/
24 
25 #include "curl_setup.h"
26 
27 #ifdef USE_WOLFSSH
28 
29 #include <limits.h>
30 
31 #include <wolfssh/ssh.h>
32 #include <wolfssh/wolfsftp.h>
33 #include "urldata.h"
34 #include "cfilters.h"
35 #include "connect.h"
36 #include "sendf.h"
37 #include "progress.h"
38 #include "curl_path.h"
39 #include "strtoofft.h"
40 #include "transfer.h"
41 #include "speedcheck.h"
42 #include "select.h"
43 #include "multiif.h"
44 #include "warnless.h"
45 #include "strdup.h"
46 
47 /* The last 3 #include files should be in this order */
48 #include "curl_printf.h"
49 #include "curl_memory.h"
50 #include "memdebug.h"
51 
52 static CURLcode wssh_connect(struct Curl_easy *data, bool *done);
53 static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done);
54 static CURLcode wssh_do(struct Curl_easy *data, bool *done);
55 #if 0
56 static CURLcode wscp_done(struct Curl_easy *data,
57                           CURLcode, bool premature);
58 static CURLcode wscp_doing(struct Curl_easy *data,
59                            bool *dophase_done);
60 static CURLcode wscp_disconnect(struct Curl_easy *data,
61                                 struct connectdata *conn,
62                                 bool dead_connection);
63 #endif
64 static CURLcode wsftp_done(struct Curl_easy *data,
65                            CURLcode, bool premature);
66 static CURLcode wsftp_doing(struct Curl_easy *data,
67                             bool *dophase_done);
68 static CURLcode wsftp_disconnect(struct Curl_easy *data,
69                                  struct connectdata *conn,
70                                  bool dead);
71 static int wssh_getsock(struct Curl_easy *data,
72                         struct connectdata *conn,
73                         curl_socket_t *sock);
74 static CURLcode wssh_setup_connection(struct Curl_easy *data,
75                                       struct connectdata *conn);
76 
77 #if 0
78 /*
79  * SCP protocol handler.
80  */
81 
82 const struct Curl_handler Curl_handler_scp = {
83   "SCP",                                /* scheme */
84   wssh_setup_connection,                /* setup_connection */
85   wssh_do,                              /* do_it */
86   wscp_done,                            /* done */
87   ZERO_NULL,                            /* do_more */
88   wssh_connect,                         /* connect_it */
89   wssh_multi_statemach,                 /* connecting */
90   wscp_doing,                           /* doing */
91   wssh_getsock,                         /* proto_getsock */
92   wssh_getsock,                         /* doing_getsock */
93   ZERO_NULL,                            /* domore_getsock */
94   wssh_getsock,                         /* perform_getsock */
95   wscp_disconnect,                      /* disconnect */
96   ZERO_NULL,                            /* write_resp */
97   ZERO_NULL,                            /* connection_check */
98   ZERO_NULL,                            /* attach connection */
99   PORT_SSH,                             /* defport */
100   CURLPROTO_SCP,                        /* protocol */
101   PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
102   | PROTOPT_NOURLQUERY                  /* flags */
103 };
104 
105 #endif
106 
107 /*
108  * SFTP protocol handler.
109  */
110 
111 const struct Curl_handler Curl_handler_sftp = {
112   "SFTP",                               /* scheme */
113   wssh_setup_connection,                /* setup_connection */
114   wssh_do,                              /* do_it */
115   wsftp_done,                           /* done */
116   ZERO_NULL,                            /* do_more */
117   wssh_connect,                         /* connect_it */
118   wssh_multi_statemach,                 /* connecting */
119   wsftp_doing,                          /* doing */
120   wssh_getsock,                         /* proto_getsock */
121   wssh_getsock,                         /* doing_getsock */
122   ZERO_NULL,                            /* domore_getsock */
123   wssh_getsock,                         /* perform_getsock */
124   wsftp_disconnect,                     /* disconnect */
125   ZERO_NULL,                            /* write_resp */
126   ZERO_NULL,                            /* connection_check */
127   ZERO_NULL,                            /* attach connection */
128   PORT_SSH,                             /* defport */
129   CURLPROTO_SFTP,                       /* protocol */
130   CURLPROTO_SFTP,                       /* family */
131   PROTOPT_DIRLOCK | PROTOPT_CLOSEACTION
132   | PROTOPT_NOURLQUERY                  /* flags */
133 };
134 
135 /*
136  * SSH State machine related code
137  */
138 /* This is the ONLY way to change SSH state! */
state(struct Curl_easy *data, sshstate nowstate)139 static void state(struct Curl_easy *data, sshstate nowstate)
140 {
141   struct connectdata *conn = data->conn;
142   struct ssh_conn *sshc = &conn->proto.sshc;
143 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
144   /* for debug purposes */
145   static const char * const names[] = {
146     "SSH_STOP",
147     "SSH_INIT",
148     "SSH_S_STARTUP",
149     "SSH_HOSTKEY",
150     "SSH_AUTHLIST",
151     "SSH_AUTH_PKEY_INIT",
152     "SSH_AUTH_PKEY",
153     "SSH_AUTH_PASS_INIT",
154     "SSH_AUTH_PASS",
155     "SSH_AUTH_AGENT_INIT",
156     "SSH_AUTH_AGENT_LIST",
157     "SSH_AUTH_AGENT",
158     "SSH_AUTH_HOST_INIT",
159     "SSH_AUTH_HOST",
160     "SSH_AUTH_KEY_INIT",
161     "SSH_AUTH_KEY",
162     "SSH_AUTH_GSSAPI",
163     "SSH_AUTH_DONE",
164     "SSH_SFTP_INIT",
165     "SSH_SFTP_REALPATH",
166     "SSH_SFTP_QUOTE_INIT",
167     "SSH_SFTP_POSTQUOTE_INIT",
168     "SSH_SFTP_QUOTE",
169     "SSH_SFTP_NEXT_QUOTE",
170     "SSH_SFTP_QUOTE_STAT",
171     "SSH_SFTP_QUOTE_SETSTAT",
172     "SSH_SFTP_QUOTE_SYMLINK",
173     "SSH_SFTP_QUOTE_MKDIR",
174     "SSH_SFTP_QUOTE_RENAME",
175     "SSH_SFTP_QUOTE_RMDIR",
176     "SSH_SFTP_QUOTE_UNLINK",
177     "SSH_SFTP_QUOTE_STATVFS",
178     "SSH_SFTP_GETINFO",
179     "SSH_SFTP_FILETIME",
180     "SSH_SFTP_TRANS_INIT",
181     "SSH_SFTP_UPLOAD_INIT",
182     "SSH_SFTP_CREATE_DIRS_INIT",
183     "SSH_SFTP_CREATE_DIRS",
184     "SSH_SFTP_CREATE_DIRS_MKDIR",
185     "SSH_SFTP_READDIR_INIT",
186     "SSH_SFTP_READDIR",
187     "SSH_SFTP_READDIR_LINK",
188     "SSH_SFTP_READDIR_BOTTOM",
189     "SSH_SFTP_READDIR_DONE",
190     "SSH_SFTP_DOWNLOAD_INIT",
191     "SSH_SFTP_DOWNLOAD_STAT",
192     "SSH_SFTP_CLOSE",
193     "SSH_SFTP_SHUTDOWN",
194     "SSH_SCP_TRANS_INIT",
195     "SSH_SCP_UPLOAD_INIT",
196     "SSH_SCP_DOWNLOAD_INIT",
197     "SSH_SCP_DOWNLOAD",
198     "SSH_SCP_DONE",
199     "SSH_SCP_SEND_EOF",
200     "SSH_SCP_WAIT_EOF",
201     "SSH_SCP_WAIT_CLOSE",
202     "SSH_SCP_CHANNEL_FREE",
203     "SSH_SESSION_DISCONNECT",
204     "SSH_SESSION_FREE",
205     "QUIT"
206   };
207 
208   /* a precaution to make sure the lists are in sync */
209   DEBUGASSERT(sizeof(names)/sizeof(names[0]) == SSH_LAST);
210 
211   if(sshc->state != nowstate) {
212     infof(data, "wolfssh %p state change from %s to %s",
213           (void *)sshc, names[sshc->state], names[nowstate]);
214   }
215 #endif
216 
217   sshc->state = nowstate;
218 }
219 
wscp_send(struct Curl_easy *data, int sockindex, const void *mem, size_t len, CURLcode *err)220 static ssize_t wscp_send(struct Curl_easy *data, int sockindex,
221                          const void *mem, size_t len, CURLcode *err)
222 {
223   ssize_t nwrite = 0;
224   (void)data;
225   (void)sockindex; /* we only support SCP on the fixed known primary socket */
226   (void)mem;
227   (void)len;
228   (void)err;
229 
230   return nwrite;
231 }
232 
wscp_recv(struct Curl_easy *data, int sockindex, char *mem, size_t len, CURLcode *err)233 static ssize_t wscp_recv(struct Curl_easy *data, int sockindex,
234                          char *mem, size_t len, CURLcode *err)
235 {
236   ssize_t nread = 0;
237   (void)data;
238   (void)sockindex; /* we only support SCP on the fixed known primary socket */
239   (void)mem;
240   (void)len;
241   (void)err;
242 
243   return nread;
244 }
245 
246 /* return number of sent bytes */
wsftp_send(struct Curl_easy *data, int sockindex, const void *mem, size_t len, CURLcode *err)247 static ssize_t wsftp_send(struct Curl_easy *data, int sockindex,
248                           const void *mem, size_t len, CURLcode *err)
249 {
250   struct connectdata *conn = data->conn;
251   struct ssh_conn *sshc = &conn->proto.sshc;
252   word32 offset[2];
253   int rc;
254   (void)sockindex;
255 
256   offset[0] = (word32)sshc->offset&0xFFFFFFFF;
257   offset[1] = (word32)(sshc->offset>>32)&0xFFFFFFFF;
258 
259   rc = wolfSSH_SFTP_SendWritePacket(sshc->ssh_session, sshc->handle,
260                                     sshc->handleSz,
261                                     &offset[0],
262                                     (byte *)mem, (word32)len);
263 
264   if(rc == WS_FATAL_ERROR)
265     rc = wolfSSH_get_error(sshc->ssh_session);
266   if(rc == WS_WANT_READ) {
267     conn->waitfor = KEEP_RECV;
268     *err = CURLE_AGAIN;
269     return -1;
270   }
271   else if(rc == WS_WANT_WRITE) {
272     conn->waitfor = KEEP_SEND;
273     *err = CURLE_AGAIN;
274     return -1;
275   }
276   if(rc < 0) {
277     failf(data, "wolfSSH_SFTP_SendWritePacket returned %d", rc);
278     return -1;
279   }
280   DEBUGASSERT(rc == (int)len);
281   infof(data, "sent %zu bytes SFTP from offset %" CURL_FORMAT_CURL_OFF_T,
282         len, sshc->offset);
283   sshc->offset += len;
284   return (ssize_t)rc;
285 }
286 
287 /*
288  * Return number of received (decrypted) bytes
289  * or <0 on error
290  */
wsftp_recv(struct Curl_easy *data, int sockindex, char *mem, size_t len, CURLcode *err)291 static ssize_t wsftp_recv(struct Curl_easy *data, int sockindex,
292                           char *mem, size_t len, CURLcode *err)
293 {
294   int rc;
295   struct connectdata *conn = data->conn;
296   struct ssh_conn *sshc = &conn->proto.sshc;
297   word32 offset[2];
298   (void)sockindex;
299 
300   offset[0] = (word32)sshc->offset&0xFFFFFFFF;
301   offset[1] = (word32)(sshc->offset>>32)&0xFFFFFFFF;
302 
303   rc = wolfSSH_SFTP_SendReadPacket(sshc->ssh_session, sshc->handle,
304                                    sshc->handleSz,
305                                    &offset[0],
306                                    (byte *)mem, (word32)len);
307   if(rc == WS_FATAL_ERROR)
308     rc = wolfSSH_get_error(sshc->ssh_session);
309   if(rc == WS_WANT_READ) {
310     conn->waitfor = KEEP_RECV;
311     *err = CURLE_AGAIN;
312     return -1;
313   }
314   else if(rc == WS_WANT_WRITE) {
315     conn->waitfor = KEEP_SEND;
316     *err = CURLE_AGAIN;
317     return -1;
318   }
319 
320   DEBUGASSERT(rc <= (int)len);
321 
322   if(rc < 0) {
323     failf(data, "wolfSSH_SFTP_SendReadPacket returned %d", rc);
324     return -1;
325   }
326   sshc->offset += len;
327 
328   return (ssize_t)rc;
329 }
330 
331 /*
332  * SSH setup and connection
333  */
wssh_setup_connection(struct Curl_easy *data, struct connectdata *conn)334 static CURLcode wssh_setup_connection(struct Curl_easy *data,
335                                       struct connectdata *conn)
336 {
337   struct SSHPROTO *ssh;
338   (void)conn;
339 
340   data->req.p.ssh = ssh = calloc(1, sizeof(struct SSHPROTO));
341   if(!ssh)
342     return CURLE_OUT_OF_MEMORY;
343 
344   return CURLE_OK;
345 }
346 
userauth(byte authtype, WS_UserAuthData* authdata, void *ctx)347 static int userauth(byte authtype,
348                     WS_UserAuthData* authdata,
349                     void *ctx)
350 {
351   struct Curl_easy *data = ctx;
352   DEBUGF(infof(data, "wolfssh callback: type %s",
353                authtype == WOLFSSH_USERAUTH_PASSWORD ? "PASSWORD" :
354                "PUBLICCKEY"));
355   if(authtype == WOLFSSH_USERAUTH_PASSWORD) {
356     authdata->sf.password.password = (byte *)data->conn->passwd;
357     authdata->sf.password.passwordSz = (word32) strlen(data->conn->passwd);
358   }
359 
360   return 0;
361 }
362 
wssh_connect(struct Curl_easy *data, bool *done)363 static CURLcode wssh_connect(struct Curl_easy *data, bool *done)
364 {
365   struct connectdata *conn = data->conn;
366   struct ssh_conn *sshc;
367   curl_socket_t sock = conn->sock[FIRSTSOCKET];
368   int rc;
369 
370   /* initialize per-handle data if not already */
371   if(!data->req.p.ssh)
372     wssh_setup_connection(data, conn);
373 
374   /* We default to persistent connections. We set this already in this connect
375      function to make the reuse checks properly be able to check this bit. */
376   connkeep(conn, "SSH default");
377 
378   if(conn->handler->protocol & CURLPROTO_SCP) {
379     conn->recv[FIRSTSOCKET] = wscp_recv;
380     conn->send[FIRSTSOCKET] = wscp_send;
381   }
382   else {
383     conn->recv[FIRSTSOCKET] = wsftp_recv;
384     conn->send[FIRSTSOCKET] = wsftp_send;
385   }
386   sshc = &conn->proto.sshc;
387   sshc->ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
388   if(!sshc->ctx) {
389     failf(data, "No wolfSSH context");
390     goto error;
391   }
392 
393   sshc->ssh_session = wolfSSH_new(sshc->ctx);
394   if(!sshc->ssh_session) {
395     failf(data, "No wolfSSH session");
396     goto error;
397   }
398 
399   rc = wolfSSH_SetUsername(sshc->ssh_session, conn->user);
400   if(rc != WS_SUCCESS) {
401     failf(data, "wolfSSH failed to set user name");
402     goto error;
403   }
404 
405   /* set callback for authentication */
406   wolfSSH_SetUserAuth(sshc->ctx, userauth);
407   wolfSSH_SetUserAuthCtx(sshc->ssh_session, data);
408 
409   rc = wolfSSH_set_fd(sshc->ssh_session, (int)sock);
410   if(rc) {
411     failf(data, "wolfSSH failed to set socket");
412     goto error;
413   }
414 
415 #if 0
416   wolfSSH_Debugging_ON();
417 #endif
418 
419   *done = TRUE;
420   if(conn->handler->protocol & CURLPROTO_SCP)
421     state(data, SSH_INIT);
422   else
423     state(data, SSH_SFTP_INIT);
424 
425   return wssh_multi_statemach(data, done);
426 error:
427   wolfSSH_free(sshc->ssh_session);
428   wolfSSH_CTX_free(sshc->ctx);
429   return CURLE_FAILED_INIT;
430 }
431 
432 /*
433  * wssh_statemach_act() runs the SSH state machine as far as it can without
434  * blocking and without reaching the end.  The data the pointer 'block' points
435  * to will be set to TRUE if the wolfssh function returns EAGAIN meaning it
436  * wants to be called again when the socket is ready
437  */
438 
wssh_statemach_act(struct Curl_easy *data, bool *block)439 static CURLcode wssh_statemach_act(struct Curl_easy *data, bool *block)
440 {
441   CURLcode result = CURLE_OK;
442   struct connectdata *conn = data->conn;
443   struct ssh_conn *sshc = &conn->proto.sshc;
444   struct SSHPROTO *sftp_scp = data->req.p.ssh;
445   WS_SFTPNAME *name;
446   int rc = 0;
447   *block = FALSE; /* we're not blocking by default */
448 
449   do {
450     switch(sshc->state) {
451     case SSH_INIT:
452       state(data, SSH_S_STARTUP);
453       break;
454 
455     case SSH_S_STARTUP:
456       rc = wolfSSH_connect(sshc->ssh_session);
457       if(rc != WS_SUCCESS)
458         rc = wolfSSH_get_error(sshc->ssh_session);
459       if(rc == WS_WANT_READ) {
460         *block = TRUE;
461         conn->waitfor = KEEP_RECV;
462         return CURLE_OK;
463       }
464       else if(rc == WS_WANT_WRITE) {
465         *block = TRUE;
466         conn->waitfor = KEEP_SEND;
467         return CURLE_OK;
468       }
469       else if(rc != WS_SUCCESS) {
470         state(data, SSH_STOP);
471         return CURLE_SSH;
472       }
473       infof(data, "wolfssh connected");
474       state(data, SSH_STOP);
475       break;
476     case SSH_STOP:
477       break;
478 
479     case SSH_SFTP_INIT:
480       rc = wolfSSH_SFTP_connect(sshc->ssh_session);
481       if(rc != WS_SUCCESS)
482         rc = wolfSSH_get_error(sshc->ssh_session);
483       if(rc == WS_WANT_READ) {
484         *block = TRUE;
485         conn->waitfor = KEEP_RECV;
486         return CURLE_OK;
487       }
488       else if(rc == WS_WANT_WRITE) {
489         *block = TRUE;
490         conn->waitfor = KEEP_SEND;
491         return CURLE_OK;
492       }
493       else if(rc == WS_SUCCESS) {
494         infof(data, "wolfssh SFTP connected");
495         state(data, SSH_SFTP_REALPATH);
496       }
497       else {
498         failf(data, "wolfssh SFTP connect error %d", rc);
499         return CURLE_SSH;
500       }
501       break;
502     case SSH_SFTP_REALPATH:
503       name = wolfSSH_SFTP_RealPath(sshc->ssh_session, (char *)".");
504       rc = wolfSSH_get_error(sshc->ssh_session);
505       if(rc == WS_WANT_READ) {
506         *block = TRUE;
507         conn->waitfor = KEEP_RECV;
508         return CURLE_OK;
509       }
510       else if(rc == WS_WANT_WRITE) {
511         *block = TRUE;
512         conn->waitfor = KEEP_SEND;
513         return CURLE_OK;
514       }
515       else if(name && (rc == WS_SUCCESS)) {
516         sshc->homedir = Curl_memdup0(name->fName, name->fSz);
517         if(!sshc->homedir)
518           sshc->actualcode = CURLE_OUT_OF_MEMORY;
519         wolfSSH_SFTPNAME_list_free(name);
520         state(data, SSH_STOP);
521         return CURLE_OK;
522       }
523       failf(data, "wolfssh SFTP realpath %d", rc);
524       return CURLE_SSH;
525 
526     case SSH_SFTP_QUOTE_INIT:
527       result = Curl_getworkingpath(data, sshc->homedir, &sftp_scp->path);
528       if(result) {
529         sshc->actualcode = result;
530         state(data, SSH_STOP);
531         break;
532       }
533 
534       if(data->set.quote) {
535         infof(data, "Sending quote commands");
536         sshc->quote_item = data->set.quote;
537         state(data, SSH_SFTP_QUOTE);
538       }
539       else {
540         state(data, SSH_SFTP_GETINFO);
541       }
542       break;
543     case SSH_SFTP_GETINFO:
544       if(data->set.get_filetime) {
545         state(data, SSH_SFTP_FILETIME);
546       }
547       else {
548         state(data, SSH_SFTP_TRANS_INIT);
549       }
550       break;
551     case SSH_SFTP_TRANS_INIT:
552       if(data->state.upload)
553         state(data, SSH_SFTP_UPLOAD_INIT);
554       else {
555         if(sftp_scp->path[strlen(sftp_scp->path)-1] == '/')
556           state(data, SSH_SFTP_READDIR_INIT);
557         else
558           state(data, SSH_SFTP_DOWNLOAD_INIT);
559       }
560       break;
561     case SSH_SFTP_UPLOAD_INIT: {
562       word32 flags;
563       WS_SFTP_FILEATRB createattrs;
564       if(data->state.resume_from) {
565         WS_SFTP_FILEATRB attrs;
566         if(data->state.resume_from < 0) {
567           rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path,
568                                  &attrs);
569           if(rc != WS_SUCCESS)
570             break;
571 
572           if(rc) {
573             data->state.resume_from = 0;
574           }
575           else {
576             curl_off_t size = ((curl_off_t)attrs.sz[1] << 32) | attrs.sz[0];
577             if(size < 0) {
578               failf(data, "Bad file size (%" CURL_FORMAT_CURL_OFF_T ")", size);
579               return CURLE_BAD_DOWNLOAD_RESUME;
580             }
581             data->state.resume_from = size;
582           }
583         }
584       }
585 
586       if(data->set.remote_append)
587         /* Try to open for append, but create if nonexisting */
588         flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_APPEND;
589       else if(data->state.resume_from > 0)
590         /* If we have restart position then open for append */
591         flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_APPEND;
592       else
593         /* Clear file before writing (normal behavior) */
594         flags = WOLFSSH_FXF_WRITE|WOLFSSH_FXF_CREAT|WOLFSSH_FXF_TRUNC;
595 
596       memset(&createattrs, 0, sizeof(createattrs));
597       createattrs.per = (word32)data->set.new_file_perms;
598       sshc->handleSz = sizeof(sshc->handle);
599       rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path,
600                              flags, &createattrs,
601                              sshc->handle, &sshc->handleSz);
602       if(rc == WS_FATAL_ERROR)
603         rc = wolfSSH_get_error(sshc->ssh_session);
604       if(rc == WS_WANT_READ) {
605         *block = TRUE;
606         conn->waitfor = KEEP_RECV;
607         return CURLE_OK;
608       }
609       else if(rc == WS_WANT_WRITE) {
610         *block = TRUE;
611         conn->waitfor = KEEP_SEND;
612         return CURLE_OK;
613       }
614       else if(rc == WS_SUCCESS) {
615         infof(data, "wolfssh SFTP open succeeded");
616       }
617       else {
618         failf(data, "wolfssh SFTP upload open failed: %d", rc);
619         return CURLE_SSH;
620       }
621       state(data, SSH_SFTP_DOWNLOAD_STAT);
622 
623       /* If we have a restart point then we need to seek to the correct
624          position. */
625       if(data->state.resume_from > 0) {
626         /* Let's read off the proper amount of bytes from the input. */
627         int seekerr = CURL_SEEKFUNC_OK;
628         if(conn->seek_func) {
629           Curl_set_in_callback(data, true);
630           seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
631                                     SEEK_SET);
632           Curl_set_in_callback(data, false);
633         }
634 
635         if(seekerr != CURL_SEEKFUNC_OK) {
636           curl_off_t passed = 0;
637 
638           if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
639             failf(data, "Could not seek stream");
640             return CURLE_FTP_COULDNT_USE_REST;
641           }
642           /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
643           do {
644             char scratch[4*1024];
645             size_t readthisamountnow =
646               (data->state.resume_from - passed >
647                 (curl_off_t)sizeof(scratch)) ?
648               sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed);
649 
650             size_t actuallyread;
651             Curl_set_in_callback(data, true);
652             actuallyread = data->state.fread_func(scratch, 1,
653                                                   readthisamountnow,
654                                                   data->state.in);
655             Curl_set_in_callback(data, false);
656 
657             passed += actuallyread;
658             if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
659               /* this checks for greater-than only to make sure that the
660                  CURL_READFUNC_ABORT return code still aborts */
661               failf(data, "Failed to read data");
662               return CURLE_FTP_COULDNT_USE_REST;
663             }
664           } while(passed < data->state.resume_from);
665         }
666 
667         /* now, decrease the size of the read */
668         if(data->state.infilesize > 0) {
669           data->state.infilesize -= data->state.resume_from;
670           data->req.size = data->state.infilesize;
671           Curl_pgrsSetUploadSize(data, data->state.infilesize);
672         }
673 
674         sshc->offset += data->state.resume_from;
675       }
676       if(data->state.infilesize > 0) {
677         data->req.size = data->state.infilesize;
678         Curl_pgrsSetUploadSize(data, data->state.infilesize);
679       }
680       /* upload data */
681       Curl_setup_transfer(data, -1, -1, FALSE, FIRSTSOCKET);
682 
683       /* not set by Curl_setup_transfer to preserve keepon bits */
684       conn->sockfd = conn->writesockfd;
685 
686       if(result) {
687         state(data, SSH_SFTP_CLOSE);
688         sshc->actualcode = result;
689       }
690       else {
691         /* store this original bitmask setup to use later on if we can't
692            figure out a "real" bitmask */
693         sshc->orig_waitfor = data->req.keepon;
694 
695         /* we want to use the _sending_ function even when the socket turns
696            out readable as the underlying libssh2 sftp send function will deal
697            with both accordingly */
698         data->state.select_bits = CURL_CSELECT_OUT;
699 
700         /* since we don't really wait for anything at this point, we want the
701            state machine to move on as soon as possible so we set a very short
702            timeout here */
703         Curl_expire(data, 0, EXPIRE_RUN_NOW);
704 
705         state(data, SSH_STOP);
706       }
707       break;
708     }
709     case SSH_SFTP_DOWNLOAD_INIT:
710       sshc->handleSz = sizeof(sshc->handle);
711       rc = wolfSSH_SFTP_Open(sshc->ssh_session, sftp_scp->path,
712                              WOLFSSH_FXF_READ, NULL,
713                              sshc->handle, &sshc->handleSz);
714       if(rc == WS_FATAL_ERROR)
715         rc = wolfSSH_get_error(sshc->ssh_session);
716       if(rc == WS_WANT_READ) {
717         *block = TRUE;
718         conn->waitfor = KEEP_RECV;
719         return CURLE_OK;
720       }
721       else if(rc == WS_WANT_WRITE) {
722         *block = TRUE;
723         conn->waitfor = KEEP_SEND;
724         return CURLE_OK;
725       }
726       else if(rc == WS_SUCCESS) {
727         infof(data, "wolfssh SFTP open succeeded");
728         state(data, SSH_SFTP_DOWNLOAD_STAT);
729         return CURLE_OK;
730       }
731 
732       failf(data, "wolfssh SFTP open failed: %d", rc);
733       return CURLE_SSH;
734 
735     case SSH_SFTP_DOWNLOAD_STAT: {
736       WS_SFTP_FILEATRB attrs;
737       curl_off_t size;
738 
739       rc = wolfSSH_SFTP_STAT(sshc->ssh_session, sftp_scp->path, &attrs);
740       if(rc == WS_FATAL_ERROR)
741         rc = wolfSSH_get_error(sshc->ssh_session);
742       if(rc == WS_WANT_READ) {
743         *block = TRUE;
744         conn->waitfor = KEEP_RECV;
745         return CURLE_OK;
746       }
747       else if(rc == WS_WANT_WRITE) {
748         *block = TRUE;
749         conn->waitfor = KEEP_SEND;
750         return CURLE_OK;
751       }
752       else if(rc == WS_SUCCESS) {
753         infof(data, "wolfssh STAT succeeded");
754       }
755       else {
756         failf(data, "wolfssh SFTP open failed: %d", rc);
757         data->req.size = -1;
758         data->req.maxdownload = -1;
759         Curl_pgrsSetDownloadSize(data, -1);
760         return CURLE_SSH;
761       }
762 
763       size = ((curl_off_t)attrs.sz[1] <<32) | attrs.sz[0];
764 
765       data->req.size = size;
766       data->req.maxdownload = size;
767       Curl_pgrsSetDownloadSize(data, size);
768 
769       infof(data, "SFTP download %" CURL_FORMAT_CURL_OFF_T " bytes", size);
770 
771       /* We cannot seek with wolfSSH so resuming and range requests are not
772          possible */
773       if(data->state.use_range || data->state.resume_from) {
774         infof(data, "wolfSSH cannot do range/seek on SFTP");
775         return CURLE_BAD_DOWNLOAD_RESUME;
776       }
777 
778       /* Setup the actual download */
779       if(data->req.size == 0) {
780         /* no data to transfer */
781         Curl_setup_transfer(data, -1, -1, FALSE, -1);
782         infof(data, "File already completely downloaded");
783         state(data, SSH_STOP);
784         break;
785       }
786       Curl_setup_transfer(data, FIRSTSOCKET, data->req.size, FALSE, -1);
787 
788       /* not set by Curl_setup_transfer to preserve keepon bits */
789       conn->writesockfd = conn->sockfd;
790 
791       /* we want to use the _receiving_ function even when the socket turns
792          out writableable as the underlying libssh2 recv function will deal
793          with both accordingly */
794       data->state.select_bits = CURL_CSELECT_IN;
795 
796       if(result) {
797         /* this should never occur; the close state should be entered
798            at the time the error occurs */
799         state(data, SSH_SFTP_CLOSE);
800         sshc->actualcode = result;
801       }
802       else {
803         state(data, SSH_STOP);
804       }
805       break;
806     }
807     case SSH_SFTP_CLOSE:
808       if(sshc->handleSz)
809         rc = wolfSSH_SFTP_Close(sshc->ssh_session, sshc->handle,
810                                 sshc->handleSz);
811       else
812         rc = WS_SUCCESS; /* directory listing */
813       if(rc == WS_WANT_READ) {
814         *block = TRUE;
815         conn->waitfor = KEEP_RECV;
816         return CURLE_OK;
817       }
818       else if(rc == WS_WANT_WRITE) {
819         *block = TRUE;
820         conn->waitfor = KEEP_SEND;
821         return CURLE_OK;
822       }
823       else if(rc == WS_SUCCESS) {
824         state(data, SSH_STOP);
825         return CURLE_OK;
826       }
827 
828       failf(data, "wolfssh SFTP CLOSE failed: %d", rc);
829       return CURLE_SSH;
830 
831     case SSH_SFTP_READDIR_INIT:
832       Curl_pgrsSetDownloadSize(data, -1);
833       if(data->req.no_body) {
834         state(data, SSH_STOP);
835         break;
836       }
837       state(data, SSH_SFTP_READDIR);
838       break;
839 
840     case SSH_SFTP_READDIR:
841       name = wolfSSH_SFTP_LS(sshc->ssh_session, sftp_scp->path);
842       if(!name)
843         rc = wolfSSH_get_error(sshc->ssh_session);
844       else
845         rc = WS_SUCCESS;
846 
847       if(rc == WS_WANT_READ) {
848         *block = TRUE;
849         conn->waitfor = KEEP_RECV;
850         return CURLE_OK;
851       }
852       else if(rc == WS_WANT_WRITE) {
853         *block = TRUE;
854         conn->waitfor = KEEP_SEND;
855         return CURLE_OK;
856       }
857       else if(name && (rc == WS_SUCCESS)) {
858         WS_SFTPNAME *origname = name;
859         result = CURLE_OK;
860         while(name) {
861           char *line = aprintf("%s\n",
862                                data->set.list_only ?
863                                name->fName : name->lName);
864           if(!line) {
865             state(data, SSH_SFTP_CLOSE);
866             sshc->actualcode = CURLE_OUT_OF_MEMORY;
867             break;
868           }
869           result = Curl_client_write(data, CLIENTWRITE_BODY,
870                                      line, strlen(line));
871           free(line);
872           if(result) {
873             sshc->actualcode = result;
874             break;
875           }
876           name = name->next;
877         }
878         wolfSSH_SFTPNAME_list_free(origname);
879         state(data, SSH_STOP);
880         return result;
881       }
882       failf(data, "wolfssh SFTP ls failed: %d", rc);
883       return CURLE_SSH;
884 
885     case SSH_SFTP_SHUTDOWN:
886       Curl_safefree(sshc->homedir);
887       wolfSSH_free(sshc->ssh_session);
888       wolfSSH_CTX_free(sshc->ctx);
889       state(data, SSH_STOP);
890       return CURLE_OK;
891     default:
892       break;
893     }
894   } while(!rc && (sshc->state != SSH_STOP));
895   return result;
896 }
897 
898 /* called repeatedly until done from multi.c */
wssh_multi_statemach(struct Curl_easy *data, bool *done)899 static CURLcode wssh_multi_statemach(struct Curl_easy *data, bool *done)
900 {
901   struct connectdata *conn = data->conn;
902   struct ssh_conn *sshc = &conn->proto.sshc;
903   CURLcode result = CURLE_OK;
904   bool block; /* we store the status and use that to provide a ssh_getsock()
905                  implementation */
906   do {
907     result = wssh_statemach_act(data, &block);
908     *done = (sshc->state == SSH_STOP) ? TRUE : FALSE;
909     /* if there's no error, it isn't done and it didn't EWOULDBLOCK, then
910        try again */
911     if(*done) {
912       DEBUGF(infof(data, "wssh_statemach_act says DONE"));
913     }
914   } while(!result && !*done && !block);
915 
916   return result;
917 }
918 
919 static
wscp_perform(struct Curl_easy *data, bool *connected, bool *dophase_done)920 CURLcode wscp_perform(struct Curl_easy *data,
921                       bool *connected,
922                       bool *dophase_done)
923 {
924   (void)data;
925   (void)connected;
926   (void)dophase_done;
927   return CURLE_OK;
928 }
929 
930 static
wsftp_perform(struct Curl_easy *data, bool *connected, bool *dophase_done)931 CURLcode wsftp_perform(struct Curl_easy *data,
932                        bool *connected,
933                        bool *dophase_done)
934 {
935   CURLcode result = CURLE_OK;
936 
937   DEBUGF(infof(data, "DO phase starts"));
938 
939   *dophase_done = FALSE; /* not done yet */
940 
941   /* start the first command in the DO phase */
942   state(data, SSH_SFTP_QUOTE_INIT);
943 
944   /* run the state-machine */
945   result = wssh_multi_statemach(data, dophase_done);
946 
947   *connected = Curl_conn_is_connected(data->conn, FIRSTSOCKET);
948 
949   if(*dophase_done) {
950     DEBUGF(infof(data, "DO phase is complete"));
951   }
952 
953   return result;
954 }
955 
956 /*
957  * The DO function is generic for both protocols.
958  */
wssh_do(struct Curl_easy *data, bool *done)959 static CURLcode wssh_do(struct Curl_easy *data, bool *done)
960 {
961   CURLcode result;
962   bool connected = 0;
963   struct connectdata *conn = data->conn;
964   struct ssh_conn *sshc = &conn->proto.sshc;
965 
966   *done = FALSE; /* default to false */
967   data->req.size = -1; /* make sure this is unknown at this point */
968   sshc->actualcode = CURLE_OK; /* reset error code */
969   sshc->secondCreateDirs = 0;   /* reset the create dir attempt state
970                                    variable */
971 
972   Curl_pgrsSetUploadCounter(data, 0);
973   Curl_pgrsSetDownloadCounter(data, 0);
974   Curl_pgrsSetUploadSize(data, -1);
975   Curl_pgrsSetDownloadSize(data, -1);
976 
977   if(conn->handler->protocol & CURLPROTO_SCP)
978     result = wscp_perform(data, &connected,  done);
979   else
980     result = wsftp_perform(data, &connected,  done);
981 
982   return result;
983 }
984 
wssh_block_statemach(struct Curl_easy *data, bool disconnect)985 static CURLcode wssh_block_statemach(struct Curl_easy *data,
986                                     bool disconnect)
987 {
988   struct connectdata *conn = data->conn;
989   struct ssh_conn *sshc = &conn->proto.sshc;
990   CURLcode result = CURLE_OK;
991 
992   while((sshc->state != SSH_STOP) && !result) {
993     bool block;
994     timediff_t left = 1000;
995     struct curltime now = Curl_now();
996 
997     result = wssh_statemach_act(data, &block);
998     if(result)
999       break;
1000 
1001     if(!disconnect) {
1002       if(Curl_pgrsUpdate(data))
1003         return CURLE_ABORTED_BY_CALLBACK;
1004 
1005       result = Curl_speedcheck(data, now);
1006       if(result)
1007         break;
1008 
1009       left = Curl_timeleft(data, NULL, FALSE);
1010       if(left < 0) {
1011         failf(data, "Operation timed out");
1012         return CURLE_OPERATION_TIMEDOUT;
1013       }
1014     }
1015 
1016     if(!result) {
1017       int dir = conn->waitfor;
1018       curl_socket_t sock = conn->sock[FIRSTSOCKET];
1019       curl_socket_t fd_read = CURL_SOCKET_BAD;
1020       curl_socket_t fd_write = CURL_SOCKET_BAD;
1021       if(dir == KEEP_RECV)
1022         fd_read = sock;
1023       else if(dir == KEEP_SEND)
1024         fd_write = sock;
1025 
1026       /* wait for the socket to become ready */
1027       (void)Curl_socket_check(fd_read, CURL_SOCKET_BAD, fd_write,
1028                               left>1000?1000:left); /* ignore result */
1029     }
1030   }
1031 
1032   return result;
1033 }
1034 
1035 /* generic done function for both SCP and SFTP called from their specific
1036    done functions */
wssh_done(struct Curl_easy *data, CURLcode status)1037 static CURLcode wssh_done(struct Curl_easy *data, CURLcode status)
1038 {
1039   CURLcode result = CURLE_OK;
1040   struct SSHPROTO *sftp_scp = data->req.p.ssh;
1041 
1042   if(!status) {
1043     /* run the state-machine */
1044     result = wssh_block_statemach(data, FALSE);
1045   }
1046   else
1047     result = status;
1048 
1049   if(sftp_scp)
1050     Curl_safefree(sftp_scp->path);
1051   if(Curl_pgrsDone(data))
1052     return CURLE_ABORTED_BY_CALLBACK;
1053 
1054   data->req.keepon = 0; /* clear all bits */
1055   return result;
1056 }
1057 
1058 #if 0
1059 static CURLcode wscp_done(struct Curl_easy *data,
1060                          CURLcode code, bool premature)
1061 {
1062   CURLcode result = CURLE_OK;
1063   (void)conn;
1064   (void)code;
1065   (void)premature;
1066 
1067   return result;
1068 }
1069 
1070 static CURLcode wscp_doing(struct Curl_easy *data,
1071                           bool *dophase_done)
1072 {
1073   CURLcode result = CURLE_OK;
1074   (void)conn;
1075   (void)dophase_done;
1076 
1077   return result;
1078 }
1079 
1080 static CURLcode wscp_disconnect(struct Curl_easy *data,
1081                                 struct connectdata *conn, bool dead_connection)
1082 {
1083   CURLcode result = CURLE_OK;
1084   (void)data;
1085   (void)conn;
1086   (void)dead_connection;
1087 
1088   return result;
1089 }
1090 #endif
1091 
wsftp_done(struct Curl_easy *data, CURLcode code, bool premature)1092 static CURLcode wsftp_done(struct Curl_easy *data,
1093                           CURLcode code, bool premature)
1094 {
1095   (void)premature;
1096   state(data, SSH_SFTP_CLOSE);
1097 
1098   return wssh_done(data, code);
1099 }
1100 
wsftp_doing(struct Curl_easy *data, bool *dophase_done)1101 static CURLcode wsftp_doing(struct Curl_easy *data,
1102                            bool *dophase_done)
1103 {
1104   CURLcode result = wssh_multi_statemach(data, dophase_done);
1105 
1106   if(*dophase_done) {
1107     DEBUGF(infof(data, "DO phase is complete"));
1108   }
1109   return result;
1110 }
1111 
wsftp_disconnect(struct Curl_easy *data, struct connectdata *conn, bool dead)1112 static CURLcode wsftp_disconnect(struct Curl_easy *data,
1113                                  struct connectdata *conn,
1114                                  bool dead)
1115 {
1116   CURLcode result = CURLE_OK;
1117   (void)dead;
1118 
1119   DEBUGF(infof(data, "SSH DISCONNECT starts now"));
1120 
1121   if(conn->proto.sshc.ssh_session) {
1122     /* only if there's a session still around to use! */
1123     state(data, SSH_SFTP_SHUTDOWN);
1124     result = wssh_block_statemach(data, TRUE);
1125   }
1126 
1127   DEBUGF(infof(data, "SSH DISCONNECT is done"));
1128   return result;
1129 }
1130 
wssh_getsock(struct Curl_easy *data, struct connectdata *conn, curl_socket_t *sock)1131 static int wssh_getsock(struct Curl_easy *data,
1132                         struct connectdata *conn,
1133                         curl_socket_t *sock)
1134 {
1135   int bitmap = GETSOCK_BLANK;
1136   int dir = conn->waitfor;
1137   (void)data;
1138   sock[0] = conn->sock[FIRSTSOCKET];
1139 
1140   if(dir == KEEP_RECV)
1141     bitmap |= GETSOCK_READSOCK(FIRSTSOCKET);
1142   else if(dir == KEEP_SEND)
1143     bitmap |= GETSOCK_WRITESOCK(FIRSTSOCKET);
1144 
1145   return bitmap;
1146 }
1147 
Curl_ssh_version(char *buffer, size_t buflen)1148 void Curl_ssh_version(char *buffer, size_t buflen)
1149 {
1150   (void)msnprintf(buffer, buflen, "wolfssh/%s", LIBWOLFSSH_VERSION_STRING);
1151 }
1152 
Curl_ssh_init(void)1153 CURLcode Curl_ssh_init(void)
1154 {
1155   if(WS_SUCCESS != wolfSSH_Init()) {
1156     DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n"));
1157     return CURLE_FAILED_INIT;
1158   }
1159 
1160   return CURLE_OK;
1161 }
Curl_ssh_cleanup(void)1162 void Curl_ssh_cleanup(void)
1163 {
1164   (void)wolfSSH_Cleanup();
1165 }
1166 
1167 #endif /* USE_WOLFSSH */
1168