1 /* 2 * Copyright (c) 2000 Markus Friedl. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 #include "includes.h" 25 RCSID("$OpenBSD: sftp-server.c,v 1.25 2001/04/05 10:42:53 markus Exp $"); 26 27 #include "buffer.h" 28 #include "bufaux.h" 29 #include "getput.h" 30 #include "log.h" 31 #include "xmalloc.h" 32 33 #include "sftp.h" 34 #include "sftp-common.h" 35 36 /* helper */ 37 #define get_int64() buffer_get_int64(&iqueue); 38 #define get_int() buffer_get_int(&iqueue); 39 #define get_string(lenp) buffer_get_string(&iqueue, lenp); 40 #define TRACE debug 41 42 /* input and output queue */ 43 Buffer iqueue; 44 Buffer oqueue; 45 46 /* Version of client */ 47 int version; 48 49 /* portable attibutes, etc. */ 50 51 typedef struct Stat Stat; 52 53 struct Stat { 54 char *name; 55 char *long_name; 56 Attrib attrib; 57 }; 58 59 int 60 errno_to_portable(int unixerrno) 61 { 62 int ret = 0; 63 64 switch (unixerrno) { 65 case 0: 66 ret = SSH2_FX_OK; 67 break; 68 case ENOENT: 69 case ENOTDIR: 70 case EBADF: 71 case ELOOP: 72 ret = SSH2_FX_NO_SUCH_FILE; 73 break; 74 case EPERM: 75 case EACCES: 76 case EFAULT: 77 ret = SSH2_FX_PERMISSION_DENIED; 78 break; 79 case ENAMETOOLONG: 80 case EINVAL: 81 ret = SSH2_FX_BAD_MESSAGE; 82 break; 83 default: 84 ret = SSH2_FX_FAILURE; 85 break; 86 } 87 return ret; 88 } 89 90 int 91 flags_from_portable(int pflags) 92 { 93 int flags = 0; 94 95 if ((pflags & SSH2_FXF_READ) && 96 (pflags & SSH2_FXF_WRITE)) { 97 flags = O_RDWR; 98 } else if (pflags & SSH2_FXF_READ) { 99 flags = O_RDONLY; 100 } else if (pflags & SSH2_FXF_WRITE) { 101 flags = O_WRONLY; 102 } 103 if (pflags & SSH2_FXF_CREAT) 104 flags |= O_CREAT; 105 if (pflags & SSH2_FXF_TRUNC) 106 flags |= O_TRUNC; 107 if (pflags & SSH2_FXF_EXCL) 108 flags |= O_EXCL; 109 return flags; 110 } 111 112 Attrib * 113 get_attrib(void) 114 { 115 return decode_attrib(&iqueue); 116 } 117 118 /* handle handles */ 119 120 typedef struct Handle Handle; 121 struct Handle { 122 int use; 123 DIR *dirp; 124 int fd; 125 char *name; 126 }; 127 128 enum { 129 HANDLE_UNUSED, 130 HANDLE_DIR, 131 HANDLE_FILE 132 }; 133 134 Handle handles[100]; 135 136 void 137 handle_init(void) 138 { 139 int i; 140 141 for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) 142 handles[i].use = HANDLE_UNUSED; 143 } 144 145 int 146 handle_new(int use, char *name, int fd, DIR *dirp) 147 { 148 int i; 149 150 for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) { 151 if (handles[i].use == HANDLE_UNUSED) { 152 handles[i].use = use; 153 handles[i].dirp = dirp; 154 handles[i].fd = fd; 155 handles[i].name = name; 156 return i; 157 } 158 } 159 return -1; 160 } 161 162 int 163 handle_is_ok(int i, int type) 164 { 165 return i >= 0 && i < sizeof(handles)/sizeof(Handle) && 166 handles[i].use == type; 167 } 168 169 int 170 handle_to_string(int handle, char **stringp, int *hlenp) 171 { 172 if (stringp == NULL || hlenp == NULL) 173 return -1; 174 *stringp = xmalloc(sizeof(int32_t)); 175 PUT_32BIT(*stringp, handle); 176 *hlenp = sizeof(int32_t); 177 return 0; 178 } 179 180 int 181 handle_from_string(char *handle, u_int hlen) 182 { 183 int val; 184 185 if (hlen != sizeof(int32_t)) 186 return -1; 187 val = GET_32BIT(handle); 188 if (handle_is_ok(val, HANDLE_FILE) || 189 handle_is_ok(val, HANDLE_DIR)) 190 return val; 191 return -1; 192 } 193 194 char * 195 handle_to_name(int handle) 196 { 197 if (handle_is_ok(handle, HANDLE_DIR)|| 198 handle_is_ok(handle, HANDLE_FILE)) 199 return handles[handle].name; 200 return NULL; 201 } 202 203 DIR * 204 handle_to_dir(int handle) 205 { 206 if (handle_is_ok(handle, HANDLE_DIR)) 207 return handles[handle].dirp; 208 return NULL; 209 } 210 211 int 212 handle_to_fd(int handle) 213 { 214 if (handle_is_ok(handle, HANDLE_FILE)) 215 return handles[handle].fd; 216 return -1; 217 } 218 219 int 220 handle_close(int handle) 221 { 222 int ret = -1; 223 224 if (handle_is_ok(handle, HANDLE_FILE)) { 225 ret = close(handles[handle].fd); 226 handles[handle].use = HANDLE_UNUSED; 227 } else if (handle_is_ok(handle, HANDLE_DIR)) { 228 ret = closedir(handles[handle].dirp); 229 handles[handle].use = HANDLE_UNUSED; 230 } else { 231 errno = ENOENT; 232 } 233 return ret; 234 } 235 236 int 237 get_handle(void) 238 { 239 char *handle; 240 int val = -1; 241 u_int hlen; 242 243 handle = get_string(&hlen); 244 if (hlen < 256) 245 val = handle_from_string(handle, hlen); 246 xfree(handle); 247 return val; 248 } 249 250 /* send replies */ 251 252 void 253 send_msg(Buffer *m) 254 { 255 int mlen = buffer_len(m); 256 257 buffer_put_int(&oqueue, mlen); 258 buffer_append(&oqueue, buffer_ptr(m), mlen); 259 buffer_consume(m, mlen); 260 } 261 262 void 263 send_status(u_int32_t id, u_int32_t error) 264 { 265 Buffer msg; 266 const char *status_messages[] = { 267 "Success", /* SSH_FX_OK */ 268 "End of file", /* SSH_FX_EOF */ 269 "No such file", /* SSH_FX_NO_SUCH_FILE */ 270 "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 271 "Failure", /* SSH_FX_FAILURE */ 272 "Bad message", /* SSH_FX_BAD_MESSAGE */ 273 "No connection", /* SSH_FX_NO_CONNECTION */ 274 "Connection lost", /* SSH_FX_CONNECTION_LOST */ 275 "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 276 "Unknown error" /* Others */ 277 }; 278 279 TRACE("sent status id %d error %d", id, error); 280 buffer_init(&msg); 281 buffer_put_char(&msg, SSH2_FXP_STATUS); 282 buffer_put_int(&msg, id); 283 buffer_put_int(&msg, error); 284 if (version >= 3) { 285 buffer_put_cstring(&msg, 286 status_messages[MIN(error,SSH2_FX_MAX)]); 287 buffer_put_cstring(&msg, ""); 288 } 289 send_msg(&msg); 290 buffer_free(&msg); 291 } 292 void 293 send_data_or_handle(char type, u_int32_t id, char *data, int dlen) 294 { 295 Buffer msg; 296 297 buffer_init(&msg); 298 buffer_put_char(&msg, type); 299 buffer_put_int(&msg, id); 300 buffer_put_string(&msg, data, dlen); 301 send_msg(&msg); 302 buffer_free(&msg); 303 } 304 305 void 306 send_data(u_int32_t id, char *data, int dlen) 307 { 308 TRACE("sent data id %d len %d", id, dlen); 309 send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 310 } 311 312 void 313 send_handle(u_int32_t id, int handle) 314 { 315 char *string; 316 int hlen; 317 318 handle_to_string(handle, &string, &hlen); 319 TRACE("sent handle id %d handle %d", id, handle); 320 send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 321 xfree(string); 322 } 323 324 void 325 send_names(u_int32_t id, int count, Stat *stats) 326 { 327 Buffer msg; 328 int i; 329 330 buffer_init(&msg); 331 buffer_put_char(&msg, SSH2_FXP_NAME); 332 buffer_put_int(&msg, id); 333 buffer_put_int(&msg, count); 334 TRACE("sent names id %d count %d", id, count); 335 for (i = 0; i < count; i++) { 336 buffer_put_cstring(&msg, stats[i].name); 337 buffer_put_cstring(&msg, stats[i].long_name); 338 encode_attrib(&msg, &stats[i].attrib); 339 } 340 send_msg(&msg); 341 buffer_free(&msg); 342 } 343 344 void 345 send_attrib(u_int32_t id, Attrib *a) 346 { 347 Buffer msg; 348 349 TRACE("sent attrib id %d have 0x%x", id, a->flags); 350 buffer_init(&msg); 351 buffer_put_char(&msg, SSH2_FXP_ATTRS); 352 buffer_put_int(&msg, id); 353 encode_attrib(&msg, a); 354 send_msg(&msg); 355 buffer_free(&msg); 356 } 357 358 /* parse incoming */ 359 360 void 361 process_init(void) 362 { 363 Buffer msg; 364 365 version = buffer_get_int(&iqueue); 366 TRACE("client version %d", version); 367 buffer_init(&msg); 368 buffer_put_char(&msg, SSH2_FXP_VERSION); 369 buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 370 send_msg(&msg); 371 buffer_free(&msg); 372 } 373 374 void 375 process_open(void) 376 { 377 u_int32_t id, pflags; 378 Attrib *a; 379 char *name; 380 int handle, fd, flags, mode, status = SSH2_FX_FAILURE; 381 382 id = get_int(); 383 name = get_string(NULL); 384 pflags = get_int(); /* portable flags */ 385 a = get_attrib(); 386 flags = flags_from_portable(pflags); 387 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; 388 TRACE("open id %d name %s flags %d mode 0%o", id, name, pflags, mode); 389 fd = open(name, flags, mode); 390 if (fd < 0) { 391 status = errno_to_portable(errno); 392 } else { 393 handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL); 394 if (handle < 0) { 395 close(fd); 396 } else { 397 send_handle(id, handle); 398 status = SSH2_FX_OK; 399 } 400 } 401 if (status != SSH2_FX_OK) 402 send_status(id, status); 403 xfree(name); 404 } 405 406 void 407 process_close(void) 408 { 409 u_int32_t id; 410 int handle, ret, status = SSH2_FX_FAILURE; 411 412 id = get_int(); 413 handle = get_handle(); 414 TRACE("close id %d handle %d", id, handle); 415 ret = handle_close(handle); 416 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 417 send_status(id, status); 418 } 419 420 void 421 process_read(void) 422 { 423 char buf[64*1024]; 424 u_int32_t id, len; 425 int handle, fd, ret, status = SSH2_FX_FAILURE; 426 u_int64_t off; 427 428 id = get_int(); 429 handle = get_handle(); 430 off = get_int64(); 431 len = get_int(); 432 433 TRACE("read id %d handle %d off %llu len %d", id, handle, 434 (unsigned long long)off, len); 435 if (len > sizeof buf) { 436 len = sizeof buf; 437 log("read change len %d", len); 438 } 439 fd = handle_to_fd(handle); 440 if (fd >= 0) { 441 if (lseek(fd, off, SEEK_SET) < 0) { 442 error("process_read: seek failed"); 443 status = errno_to_portable(errno); 444 } else { 445 ret = read(fd, buf, len); 446 if (ret < 0) { 447 status = errno_to_portable(errno); 448 } else if (ret == 0) { 449 status = SSH2_FX_EOF; 450 } else { 451 send_data(id, buf, ret); 452 status = SSH2_FX_OK; 453 } 454 } 455 } 456 if (status != SSH2_FX_OK) 457 send_status(id, status); 458 } 459 460 void 461 process_write(void) 462 { 463 u_int32_t id; 464 u_int64_t off; 465 u_int len; 466 int handle, fd, ret, status = SSH2_FX_FAILURE; 467 char *data; 468 469 id = get_int(); 470 handle = get_handle(); 471 off = get_int64(); 472 data = get_string(&len); 473 474 TRACE("write id %d handle %d off %llu len %d", id, handle, 475 (unsigned long long)off, len); 476 fd = handle_to_fd(handle); 477 if (fd >= 0) { 478 if (lseek(fd, off, SEEK_SET) < 0) { 479 status = errno_to_portable(errno); 480 error("process_write: seek failed"); 481 } else { 482 /* XXX ATOMICIO ? */ 483 ret = write(fd, data, len); 484 if (ret == -1) { 485 error("process_write: write failed"); 486 status = errno_to_portable(errno); 487 } else if (ret == len) { 488 status = SSH2_FX_OK; 489 } else { 490 log("nothing at all written"); 491 } 492 } 493 } 494 send_status(id, status); 495 xfree(data); 496 } 497 498 void 499 process_do_stat(int do_lstat) 500 { 501 Attrib a; 502 struct stat st; 503 u_int32_t id; 504 char *name; 505 int ret, status = SSH2_FX_FAILURE; 506 507 id = get_int(); 508 name = get_string(NULL); 509 TRACE("%sstat id %d name %s", do_lstat ? "l" : "", id, name); 510 ret = do_lstat ? lstat(name, &st) : stat(name, &st); 511 if (ret < 0) { 512 status = errno_to_portable(errno); 513 } else { 514 stat_to_attrib(&st, &a); 515 send_attrib(id, &a); 516 status = SSH2_FX_OK; 517 } 518 if (status != SSH2_FX_OK) 519 send_status(id, status); 520 xfree(name); 521 } 522 523 void 524 process_stat(void) 525 { 526 process_do_stat(0); 527 } 528 529 void 530 process_lstat(void) 531 { 532 process_do_stat(1); 533 } 534 535 void 536 process_fstat(void) 537 { 538 Attrib a; 539 struct stat st; 540 u_int32_t id; 541 int fd, ret, handle, status = SSH2_FX_FAILURE; 542 543 id = get_int(); 544 handle = get_handle(); 545 TRACE("fstat id %d handle %d", id, handle); 546 fd = handle_to_fd(handle); 547 if (fd >= 0) { 548 ret = fstat(fd, &st); 549 if (ret < 0) { 550 status = errno_to_portable(errno); 551 } else { 552 stat_to_attrib(&st, &a); 553 send_attrib(id, &a); 554 status = SSH2_FX_OK; 555 } 556 } 557 if (status != SSH2_FX_OK) 558 send_status(id, status); 559 } 560 561 struct timeval * 562 attrib_to_tv(Attrib *a) 563 { 564 static struct timeval tv[2]; 565 566 tv[0].tv_sec = a->atime; 567 tv[0].tv_usec = 0; 568 tv[1].tv_sec = a->mtime; 569 tv[1].tv_usec = 0; 570 return tv; 571 } 572 573 void 574 process_setstat(void) 575 { 576 Attrib *a; 577 u_int32_t id; 578 char *name; 579 int ret; 580 int status = SSH2_FX_OK; 581 582 id = get_int(); 583 name = get_string(NULL); 584 a = get_attrib(); 585 TRACE("setstat id %d name %s", id, name); 586 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 587 ret = chmod(name, a->perm & 0777); 588 if (ret == -1) 589 status = errno_to_portable(errno); 590 } 591 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 592 ret = utimes(name, attrib_to_tv(a)); 593 if (ret == -1) 594 status = errno_to_portable(errno); 595 } 596 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 597 ret = chown(name, a->uid, a->gid); 598 if (ret == -1) 599 status = errno_to_portable(errno); 600 } 601 send_status(id, status); 602 xfree(name); 603 } 604 605 void 606 process_fsetstat(void) 607 { 608 Attrib *a; 609 u_int32_t id; 610 int handle, fd, ret; 611 int status = SSH2_FX_OK; 612 613 id = get_int(); 614 handle = get_handle(); 615 a = get_attrib(); 616 TRACE("fsetstat id %d handle %d", id, handle); 617 fd = handle_to_fd(handle); 618 if (fd < 0) { 619 status = SSH2_FX_FAILURE; 620 } else { 621 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 622 ret = fchmod(fd, a->perm & 0777); 623 if (ret == -1) 624 status = errno_to_portable(errno); 625 } 626 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 627 ret = futimes(fd, attrib_to_tv(a)); 628 if (ret == -1) 629 status = errno_to_portable(errno); 630 } 631 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 632 ret = fchown(fd, a->uid, a->gid); 633 if (ret == -1) 634 status = errno_to_portable(errno); 635 } 636 } 637 send_status(id, status); 638 } 639 640 void 641 process_opendir(void) 642 { 643 DIR *dirp = NULL; 644 char *path; 645 int handle, status = SSH2_FX_FAILURE; 646 u_int32_t id; 647 648 id = get_int(); 649 path = get_string(NULL); 650 TRACE("opendir id %d path %s", id, path); 651 dirp = opendir(path); 652 if (dirp == NULL) { 653 status = errno_to_portable(errno); 654 } else { 655 handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp); 656 if (handle < 0) { 657 closedir(dirp); 658 } else { 659 send_handle(id, handle); 660 status = SSH2_FX_OK; 661 } 662 663 } 664 if (status != SSH2_FX_OK) 665 send_status(id, status); 666 xfree(path); 667 } 668 669 /* 670 * drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh 671 */ 672 char * 673 ls_file(char *name, struct stat *st) 674 { 675 int sz = 0; 676 struct passwd *pw; 677 struct group *gr; 678 struct tm *ltime = localtime(&st->st_mtime); 679 char *user, *group; 680 char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1]; 681 682 strmode(st->st_mode, mode); 683 if ((pw = getpwuid(st->st_uid)) != NULL) { 684 user = pw->pw_name; 685 } else { 686 snprintf(ubuf, sizeof ubuf, "%d", st->st_uid); 687 user = ubuf; 688 } 689 if ((gr = getgrgid(st->st_gid)) != NULL) { 690 group = gr->gr_name; 691 } else { 692 snprintf(gbuf, sizeof gbuf, "%d", st->st_gid); 693 group = gbuf; 694 } 695 if (ltime != NULL) { 696 if (time(NULL) - st->st_mtime < (365*24*60*60)/2) 697 sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime); 698 else 699 sz = strftime(tbuf, sizeof tbuf, "%b %e %Y", ltime); 700 } 701 if (sz == 0) 702 tbuf[0] = '\0'; 703 snprintf(buf, sizeof buf, "%s %3d %-8.8s %-8.8s %8llu %s %s", mode, 704 st->st_nlink, user, group, (unsigned long long)st->st_size, tbuf, name); 705 return xstrdup(buf); 706 } 707 708 void 709 process_readdir(void) 710 { 711 DIR *dirp; 712 struct dirent *dp; 713 char *path; 714 int handle; 715 u_int32_t id; 716 717 id = get_int(); 718 handle = get_handle(); 719 TRACE("readdir id %d handle %d", id, handle); 720 dirp = handle_to_dir(handle); 721 path = handle_to_name(handle); 722 if (dirp == NULL || path == NULL) { 723 send_status(id, SSH2_FX_FAILURE); 724 } else { 725 struct stat st; 726 char pathname[1024]; 727 Stat *stats; 728 int nstats = 10, count = 0, i; 729 stats = xmalloc(nstats * sizeof(Stat)); 730 while ((dp = readdir(dirp)) != NULL) { 731 if (count >= nstats) { 732 nstats *= 2; 733 stats = xrealloc(stats, nstats * sizeof(Stat)); 734 } 735 /* XXX OVERFLOW ? */ 736 snprintf(pathname, sizeof pathname, 737 "%s/%s", path, dp->d_name); 738 if (lstat(pathname, &st) < 0) 739 continue; 740 stat_to_attrib(&st, &(stats[count].attrib)); 741 stats[count].name = xstrdup(dp->d_name); 742 stats[count].long_name = ls_file(dp->d_name, &st); 743 count++; 744 /* send up to 100 entries in one message */ 745 /* XXX check packet size instead */ 746 if (count == 100) 747 break; 748 } 749 if (count > 0) { 750 send_names(id, count, stats); 751 for(i = 0; i < count; i++) { 752 xfree(stats[i].name); 753 xfree(stats[i].long_name); 754 } 755 } else { 756 send_status(id, SSH2_FX_EOF); 757 } 758 xfree(stats); 759 } 760 } 761 762 void 763 process_remove(void) 764 { 765 char *name; 766 u_int32_t id; 767 int status = SSH2_FX_FAILURE; 768 int ret; 769 770 id = get_int(); 771 name = get_string(NULL); 772 TRACE("remove id %d name %s", id, name); 773 ret = unlink(name); 774 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 775 send_status(id, status); 776 xfree(name); 777 } 778 779 void 780 process_mkdir(void) 781 { 782 Attrib *a; 783 u_int32_t id; 784 char *name; 785 int ret, mode, status = SSH2_FX_FAILURE; 786 787 id = get_int(); 788 name = get_string(NULL); 789 a = get_attrib(); 790 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 791 a->perm & 0777 : 0777; 792 TRACE("mkdir id %d name %s mode 0%o", id, name, mode); 793 ret = mkdir(name, mode); 794 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 795 send_status(id, status); 796 xfree(name); 797 } 798 799 void 800 process_rmdir(void) 801 { 802 u_int32_t id; 803 char *name; 804 int ret, status; 805 806 id = get_int(); 807 name = get_string(NULL); 808 TRACE("rmdir id %d name %s", id, name); 809 ret = rmdir(name); 810 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 811 send_status(id, status); 812 xfree(name); 813 } 814 815 void 816 process_realpath(void) 817 { 818 char resolvedname[MAXPATHLEN]; 819 u_int32_t id; 820 char *path; 821 822 id = get_int(); 823 path = get_string(NULL); 824 if (path[0] == '\0') { 825 xfree(path); 826 path = xstrdup("."); 827 } 828 TRACE("realpath id %d path %s", id, path); 829 if (realpath(path, resolvedname) == NULL) { 830 send_status(id, errno_to_portable(errno)); 831 } else { 832 Stat s; 833 attrib_clear(&s.attrib); 834 s.name = s.long_name = resolvedname; 835 send_names(id, 1, &s); 836 } 837 xfree(path); 838 } 839 840 void 841 process_rename(void) 842 { 843 u_int32_t id; 844 struct stat st; 845 char *oldpath, *newpath; 846 int ret, status = SSH2_FX_FAILURE; 847 848 id = get_int(); 849 oldpath = get_string(NULL); 850 newpath = get_string(NULL); 851 TRACE("rename id %d old %s new %s", id, oldpath, newpath); 852 /* fail if 'newpath' exists */ 853 if (stat(newpath, &st) == -1) { 854 ret = rename(oldpath, newpath); 855 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 856 } 857 send_status(id, status); 858 xfree(oldpath); 859 xfree(newpath); 860 } 861 862 void 863 process_readlink(void) 864 { 865 u_int32_t id; 866 char link[MAXPATHLEN]; 867 char *path; 868 869 id = get_int(); 870 path = get_string(NULL); 871 TRACE("readlink id %d path %s", id, path); 872 if (readlink(path, link, sizeof(link) - 1) == -1) 873 send_status(id, errno_to_portable(errno)); 874 else { 875 Stat s; 876 877 link[sizeof(link) - 1] = '\0'; 878 attrib_clear(&s.attrib); 879 s.name = s.long_name = link; 880 send_names(id, 1, &s); 881 } 882 xfree(path); 883 } 884 885 void 886 process_symlink(void) 887 { 888 u_int32_t id; 889 struct stat st; 890 char *oldpath, *newpath; 891 int ret, status = SSH2_FX_FAILURE; 892 893 id = get_int(); 894 oldpath = get_string(NULL); 895 newpath = get_string(NULL); 896 TRACE("symlink id %d old %s new %s", id, oldpath, newpath); 897 /* fail if 'newpath' exists */ 898 if (stat(newpath, &st) == -1) { 899 ret = symlink(oldpath, newpath); 900 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 901 } 902 send_status(id, status); 903 xfree(oldpath); 904 xfree(newpath); 905 } 906 907 void 908 process_extended(void) 909 { 910 u_int32_t id; 911 char *request; 912 913 id = get_int(); 914 request = get_string(NULL); 915 send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 916 xfree(request); 917 } 918 919 /* stolen from ssh-agent */ 920 921 void 922 process(void) 923 { 924 u_int msg_len; 925 u_int type; 926 u_char *cp; 927 928 if (buffer_len(&iqueue) < 5) 929 return; /* Incomplete message. */ 930 cp = (u_char *) buffer_ptr(&iqueue); 931 msg_len = GET_32BIT(cp); 932 if (msg_len > 256 * 1024) { 933 error("bad message "); 934 exit(11); 935 } 936 if (buffer_len(&iqueue) < msg_len + 4) 937 return; 938 buffer_consume(&iqueue, 4); 939 type = buffer_get_char(&iqueue); 940 switch (type) { 941 case SSH2_FXP_INIT: 942 process_init(); 943 break; 944 case SSH2_FXP_OPEN: 945 process_open(); 946 break; 947 case SSH2_FXP_CLOSE: 948 process_close(); 949 break; 950 case SSH2_FXP_READ: 951 process_read(); 952 break; 953 case SSH2_FXP_WRITE: 954 process_write(); 955 break; 956 case SSH2_FXP_LSTAT: 957 process_lstat(); 958 break; 959 case SSH2_FXP_FSTAT: 960 process_fstat(); 961 break; 962 case SSH2_FXP_SETSTAT: 963 process_setstat(); 964 break; 965 case SSH2_FXP_FSETSTAT: 966 process_fsetstat(); 967 break; 968 case SSH2_FXP_OPENDIR: 969 process_opendir(); 970 break; 971 case SSH2_FXP_READDIR: 972 process_readdir(); 973 break; 974 case SSH2_FXP_REMOVE: 975 process_remove(); 976 break; 977 case SSH2_FXP_MKDIR: 978 process_mkdir(); 979 break; 980 case SSH2_FXP_RMDIR: 981 process_rmdir(); 982 break; 983 case SSH2_FXP_REALPATH: 984 process_realpath(); 985 break; 986 case SSH2_FXP_STAT: 987 process_stat(); 988 break; 989 case SSH2_FXP_RENAME: 990 process_rename(); 991 break; 992 case SSH2_FXP_READLINK: 993 process_readlink(); 994 break; 995 case SSH2_FXP_SYMLINK: 996 process_symlink(); 997 break; 998 case SSH2_FXP_EXTENDED: 999 process_extended(); 1000 break; 1001 default: 1002 error("Unknown message %d", type); 1003 break; 1004 } 1005 } 1006 1007 int 1008 main(int ac, char **av) 1009 { 1010 fd_set *rset, *wset; 1011 int in, out, max; 1012 ssize_t len, olen, set_size; 1013 1014 /* XXX should use getopt */ 1015 1016 handle_init(); 1017 1018 #ifdef DEBUG_SFTP_SERVER 1019 log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); 1020 #endif 1021 1022 in = dup(STDIN_FILENO); 1023 out = dup(STDOUT_FILENO); 1024 1025 max = 0; 1026 if (in > max) 1027 max = in; 1028 if (out > max) 1029 max = out; 1030 1031 buffer_init(&iqueue); 1032 buffer_init(&oqueue); 1033 1034 set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 1035 rset = (fd_set *)xmalloc(set_size); 1036 wset = (fd_set *)xmalloc(set_size); 1037 1038 for (;;) { 1039 memset(rset, 0, set_size); 1040 memset(wset, 0, set_size); 1041 1042 FD_SET(in, rset); 1043 olen = buffer_len(&oqueue); 1044 if (olen > 0) 1045 FD_SET(out, wset); 1046 1047 if (select(max+1, rset, wset, NULL, NULL) < 0) { 1048 if (errno == EINTR) 1049 continue; 1050 exit(2); 1051 } 1052 1053 /* copy stdin to iqueue */ 1054 if (FD_ISSET(in, rset)) { 1055 char buf[4*4096]; 1056 len = read(in, buf, sizeof buf); 1057 if (len == 0) { 1058 debug("read eof"); 1059 exit(0); 1060 } else if (len < 0) { 1061 error("read error"); 1062 exit(1); 1063 } else { 1064 buffer_append(&iqueue, buf, len); 1065 } 1066 } 1067 /* send oqueue to stdout */ 1068 if (FD_ISSET(out, wset)) { 1069 len = write(out, buffer_ptr(&oqueue), olen); 1070 if (len < 0) { 1071 error("write error"); 1072 exit(1); 1073 } else { 1074 buffer_consume(&oqueue, len); 1075 } 1076 } 1077 /* process requests from client */ 1078 process(); 1079 } 1080 } 1081