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