sftp-server.c revision 98675
1/* 2 * Copyright (c) 2000, 2001, 2002 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" 25RCSID("$OpenBSD: sftp-server.c,v 1.35 2002/06/06 17:30:11 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 */ 43Buffer iqueue; 44Buffer oqueue; 45 46/* Version of client */ 47int version; 48 49/* portable attibutes, etc. */ 50 51typedef struct Stat Stat; 52 53struct Stat { 54 char *name; 55 char *long_name; 56 Attrib attrib; 57}; 58 59static int 60errno_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 90static int 91flags_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 112static Attrib * 113get_attrib(void) 114{ 115 return decode_attrib(&iqueue); 116} 117 118/* handle handles */ 119 120typedef struct Handle Handle; 121struct Handle { 122 int use; 123 DIR *dirp; 124 int fd; 125 char *name; 126}; 127 128enum { 129 HANDLE_UNUSED, 130 HANDLE_DIR, 131 HANDLE_FILE 132}; 133 134Handle handles[100]; 135 136static void 137handle_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 145static int 146handle_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 162static int 163handle_is_ok(int i, int type) 164{ 165 return i >= 0 && i < sizeof(handles)/sizeof(Handle) && 166 handles[i].use == type; 167} 168 169static int 170handle_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 180static int 181handle_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 194static char * 195handle_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 203static DIR * 204handle_to_dir(int handle) 205{ 206 if (handle_is_ok(handle, HANDLE_DIR)) 207 return handles[handle].dirp; 208 return NULL; 209} 210 211static int 212handle_to_fd(int handle) 213{ 214 if (handle_is_ok(handle, HANDLE_FILE)) 215 return handles[handle].fd; 216 return -1; 217} 218 219static int 220handle_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 236static int 237get_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 252static void 253send_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 262static void 263send_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} 292static void 293send_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 305static void 306send_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 312static void 313send_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 324static void 325send_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 344static void 345send_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 360static void 361process_init(void) 362{ 363 Buffer msg; 364 365 version = get_int(); 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 374static void 375process_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 406static void 407process_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 420static void 421process_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 460static void 461process_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 498static void 499process_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 523static void 524process_stat(void) 525{ 526 process_do_stat(0); 527} 528 529static void 530process_lstat(void) 531{ 532 process_do_stat(1); 533} 534 535static void 536process_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 561static struct timeval * 562attrib_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 573static void 574process_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_SIZE) { 587 ret = truncate(name, a->size); 588 if (ret == -1) 589 status = errno_to_portable(errno); 590 } 591 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 592 ret = chmod(name, a->perm & 0777); 593 if (ret == -1) 594 status = errno_to_portable(errno); 595 } 596 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 597 ret = utimes(name, attrib_to_tv(a)); 598 if (ret == -1) 599 status = errno_to_portable(errno); 600 } 601 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 602 ret = chown(name, a->uid, a->gid); 603 if (ret == -1) 604 status = errno_to_portable(errno); 605 } 606 send_status(id, status); 607 xfree(name); 608} 609 610static void 611process_fsetstat(void) 612{ 613 Attrib *a; 614 u_int32_t id; 615 int handle, fd, ret; 616 int status = SSH2_FX_OK; 617 618 id = get_int(); 619 handle = get_handle(); 620 a = get_attrib(); 621 TRACE("fsetstat id %d handle %d", id, handle); 622 fd = handle_to_fd(handle); 623 if (fd < 0) { 624 status = SSH2_FX_FAILURE; 625 } else { 626 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 627 ret = ftruncate(fd, a->size); 628 if (ret == -1) 629 status = errno_to_portable(errno); 630 } 631 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 632 ret = fchmod(fd, a->perm & 0777); 633 if (ret == -1) 634 status = errno_to_portable(errno); 635 } 636 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 637 ret = futimes(fd, attrib_to_tv(a)); 638 if (ret == -1) 639 status = errno_to_portable(errno); 640 } 641 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 642 ret = fchown(fd, a->uid, a->gid); 643 if (ret == -1) 644 status = errno_to_portable(errno); 645 } 646 } 647 send_status(id, status); 648} 649 650static void 651process_opendir(void) 652{ 653 DIR *dirp = NULL; 654 char *path; 655 int handle, status = SSH2_FX_FAILURE; 656 u_int32_t id; 657 658 id = get_int(); 659 path = get_string(NULL); 660 TRACE("opendir id %d path %s", id, path); 661 dirp = opendir(path); 662 if (dirp == NULL) { 663 status = errno_to_portable(errno); 664 } else { 665 handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp); 666 if (handle < 0) { 667 closedir(dirp); 668 } else { 669 send_handle(id, handle); 670 status = SSH2_FX_OK; 671 } 672 673 } 674 if (status != SSH2_FX_OK) 675 send_status(id, status); 676 xfree(path); 677} 678 679/* 680 * drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh 681 */ 682static char * 683ls_file(char *name, struct stat *st) 684{ 685 int ulen, glen, sz = 0; 686 struct passwd *pw; 687 struct group *gr; 688 struct tm *ltime = localtime(&st->st_mtime); 689 char *user, *group; 690 char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1]; 691 692 strmode(st->st_mode, mode); 693 if ((pw = getpwuid(st->st_uid)) != NULL) { 694 user = pw->pw_name; 695 } else { 696 snprintf(ubuf, sizeof ubuf, "%d", st->st_uid); 697 user = ubuf; 698 } 699 if ((gr = getgrgid(st->st_gid)) != NULL) { 700 group = gr->gr_name; 701 } else { 702 snprintf(gbuf, sizeof gbuf, "%d", st->st_gid); 703 group = gbuf; 704 } 705 if (ltime != NULL) { 706 if (time(NULL) - st->st_mtime < (365*24*60*60)/2) 707 sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime); 708 else 709 sz = strftime(tbuf, sizeof tbuf, "%b %e %Y", ltime); 710 } 711 if (sz == 0) 712 tbuf[0] = '\0'; 713 ulen = MAX(strlen(user), 8); 714 glen = MAX(strlen(group), 8); 715 snprintf(buf, sizeof buf, "%s %3d %-*s %-*s %8llu %s %s", mode, 716 st->st_nlink, ulen, user, glen, group, 717 (unsigned long long)st->st_size, tbuf, name); 718 return xstrdup(buf); 719} 720 721static void 722process_readdir(void) 723{ 724 DIR *dirp; 725 struct dirent *dp; 726 char *path; 727 int handle; 728 u_int32_t id; 729 730 id = get_int(); 731 handle = get_handle(); 732 TRACE("readdir id %d handle %d", id, handle); 733 dirp = handle_to_dir(handle); 734 path = handle_to_name(handle); 735 if (dirp == NULL || path == NULL) { 736 send_status(id, SSH2_FX_FAILURE); 737 } else { 738 struct stat st; 739 char pathname[1024]; 740 Stat *stats; 741 int nstats = 10, count = 0, i; 742 stats = xmalloc(nstats * sizeof(Stat)); 743 while ((dp = readdir(dirp)) != NULL) { 744 if (count >= nstats) { 745 nstats *= 2; 746 stats = xrealloc(stats, nstats * sizeof(Stat)); 747 } 748/* XXX OVERFLOW ? */ 749 snprintf(pathname, sizeof pathname, "%s%s%s", path, 750 strcmp(path, "/") ? "/" : "", dp->d_name); 751 if (lstat(pathname, &st) < 0) 752 continue; 753 stat_to_attrib(&st, &(stats[count].attrib)); 754 stats[count].name = xstrdup(dp->d_name); 755 stats[count].long_name = ls_file(dp->d_name, &st); 756 count++; 757 /* send up to 100 entries in one message */ 758 /* XXX check packet size instead */ 759 if (count == 100) 760 break; 761 } 762 if (count > 0) { 763 send_names(id, count, stats); 764 for (i = 0; i < count; i++) { 765 xfree(stats[i].name); 766 xfree(stats[i].long_name); 767 } 768 } else { 769 send_status(id, SSH2_FX_EOF); 770 } 771 xfree(stats); 772 } 773} 774 775static void 776process_remove(void) 777{ 778 char *name; 779 u_int32_t id; 780 int status = SSH2_FX_FAILURE; 781 int ret; 782 783 id = get_int(); 784 name = get_string(NULL); 785 TRACE("remove id %d name %s", id, name); 786 ret = unlink(name); 787 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 788 send_status(id, status); 789 xfree(name); 790} 791 792static void 793process_mkdir(void) 794{ 795 Attrib *a; 796 u_int32_t id; 797 char *name; 798 int ret, mode, status = SSH2_FX_FAILURE; 799 800 id = get_int(); 801 name = get_string(NULL); 802 a = get_attrib(); 803 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 804 a->perm & 0777 : 0777; 805 TRACE("mkdir id %d name %s mode 0%o", id, name, mode); 806 ret = mkdir(name, mode); 807 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 808 send_status(id, status); 809 xfree(name); 810} 811 812static void 813process_rmdir(void) 814{ 815 u_int32_t id; 816 char *name; 817 int ret, status; 818 819 id = get_int(); 820 name = get_string(NULL); 821 TRACE("rmdir id %d name %s", id, name); 822 ret = rmdir(name); 823 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 824 send_status(id, status); 825 xfree(name); 826} 827 828static void 829process_realpath(void) 830{ 831 char resolvedname[MAXPATHLEN]; 832 u_int32_t id; 833 char *path; 834 835 id = get_int(); 836 path = get_string(NULL); 837 if (path[0] == '\0') { 838 xfree(path); 839 path = xstrdup("."); 840 } 841 TRACE("realpath id %d path %s", id, path); 842 if (realpath(path, resolvedname) == NULL) { 843 send_status(id, errno_to_portable(errno)); 844 } else { 845 Stat s; 846 attrib_clear(&s.attrib); 847 s.name = s.long_name = resolvedname; 848 send_names(id, 1, &s); 849 } 850 xfree(path); 851} 852 853static void 854process_rename(void) 855{ 856 u_int32_t id; 857 struct stat st; 858 char *oldpath, *newpath; 859 int ret, status = SSH2_FX_FAILURE; 860 861 id = get_int(); 862 oldpath = get_string(NULL); 863 newpath = get_string(NULL); 864 TRACE("rename id %d old %s new %s", id, oldpath, newpath); 865 /* fail if 'newpath' exists */ 866 if (stat(newpath, &st) == -1) { 867 ret = rename(oldpath, newpath); 868 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 869 } 870 send_status(id, status); 871 xfree(oldpath); 872 xfree(newpath); 873} 874 875static void 876process_readlink(void) 877{ 878 u_int32_t id; 879 int len; 880 char link[MAXPATHLEN]; 881 char *path; 882 883 id = get_int(); 884 path = get_string(NULL); 885 TRACE("readlink id %d path %s", id, path); 886 if ((len = readlink(path, link, sizeof(link) - 1)) == -1) 887 send_status(id, errno_to_portable(errno)); 888 else { 889 Stat s; 890 891 link[len] = '\0'; 892 attrib_clear(&s.attrib); 893 s.name = s.long_name = link; 894 send_names(id, 1, &s); 895 } 896 xfree(path); 897} 898 899static void 900process_symlink(void) 901{ 902 u_int32_t id; 903 struct stat st; 904 char *oldpath, *newpath; 905 int ret, status = SSH2_FX_FAILURE; 906 907 id = get_int(); 908 oldpath = get_string(NULL); 909 newpath = get_string(NULL); 910 TRACE("symlink id %d old %s new %s", id, oldpath, newpath); 911 /* fail if 'newpath' exists */ 912 if (stat(newpath, &st) == -1) { 913 ret = symlink(oldpath, newpath); 914 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 915 } 916 send_status(id, status); 917 xfree(oldpath); 918 xfree(newpath); 919} 920 921static void 922process_extended(void) 923{ 924 u_int32_t id; 925 char *request; 926 927 id = get_int(); 928 request = get_string(NULL); 929 send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 930 xfree(request); 931} 932 933/* stolen from ssh-agent */ 934 935static void 936process(void) 937{ 938 u_int msg_len; 939 u_int buf_len; 940 u_int consumed; 941 u_int type; 942 u_char *cp; 943 944 buf_len = buffer_len(&iqueue); 945 if (buf_len < 5) 946 return; /* Incomplete message. */ 947 cp = buffer_ptr(&iqueue); 948 msg_len = GET_32BIT(cp); 949 if (msg_len > 256 * 1024) { 950 error("bad message "); 951 exit(11); 952 } 953 if (buf_len < msg_len + 4) 954 return; 955 buffer_consume(&iqueue, 4); 956 buf_len -= 4; 957 type = buffer_get_char(&iqueue); 958 switch (type) { 959 case SSH2_FXP_INIT: 960 process_init(); 961 break; 962 case SSH2_FXP_OPEN: 963 process_open(); 964 break; 965 case SSH2_FXP_CLOSE: 966 process_close(); 967 break; 968 case SSH2_FXP_READ: 969 process_read(); 970 break; 971 case SSH2_FXP_WRITE: 972 process_write(); 973 break; 974 case SSH2_FXP_LSTAT: 975 process_lstat(); 976 break; 977 case SSH2_FXP_FSTAT: 978 process_fstat(); 979 break; 980 case SSH2_FXP_SETSTAT: 981 process_setstat(); 982 break; 983 case SSH2_FXP_FSETSTAT: 984 process_fsetstat(); 985 break; 986 case SSH2_FXP_OPENDIR: 987 process_opendir(); 988 break; 989 case SSH2_FXP_READDIR: 990 process_readdir(); 991 break; 992 case SSH2_FXP_REMOVE: 993 process_remove(); 994 break; 995 case SSH2_FXP_MKDIR: 996 process_mkdir(); 997 break; 998 case SSH2_FXP_RMDIR: 999 process_rmdir(); 1000 break; 1001 case SSH2_FXP_REALPATH: 1002 process_realpath(); 1003 break; 1004 case SSH2_FXP_STAT: 1005 process_stat(); 1006 break; 1007 case SSH2_FXP_RENAME: 1008 process_rename(); 1009 break; 1010 case SSH2_FXP_READLINK: 1011 process_readlink(); 1012 break; 1013 case SSH2_FXP_SYMLINK: 1014 process_symlink(); 1015 break; 1016 case SSH2_FXP_EXTENDED: 1017 process_extended(); 1018 break; 1019 default: 1020 error("Unknown message %d", type); 1021 break; 1022 } 1023 /* discard the remaining bytes from the current packet */ 1024 if (buf_len < buffer_len(&iqueue)) 1025 fatal("iqueue grows"); 1026 consumed = buf_len - buffer_len(&iqueue); 1027 if (msg_len < consumed) 1028 fatal("msg_len %d < consumed %d", msg_len, consumed); 1029 if (msg_len > consumed) 1030 buffer_consume(&iqueue, msg_len - consumed); 1031} 1032 1033int 1034main(int ac, char **av) 1035{ 1036 fd_set *rset, *wset; 1037 int in, out, max; 1038 ssize_t len, olen, set_size; 1039 1040 /* XXX should use getopt */ 1041 1042 handle_init(); 1043 1044#ifdef DEBUG_SFTP_SERVER 1045 log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); 1046#endif 1047 1048 in = dup(STDIN_FILENO); 1049 out = dup(STDOUT_FILENO); 1050 1051 max = 0; 1052 if (in > max) 1053 max = in; 1054 if (out > max) 1055 max = out; 1056 1057 buffer_init(&iqueue); 1058 buffer_init(&oqueue); 1059 1060 set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 1061 rset = (fd_set *)xmalloc(set_size); 1062 wset = (fd_set *)xmalloc(set_size); 1063 1064 for (;;) { 1065 memset(rset, 0, set_size); 1066 memset(wset, 0, set_size); 1067 1068 FD_SET(in, rset); 1069 olen = buffer_len(&oqueue); 1070 if (olen > 0) 1071 FD_SET(out, wset); 1072 1073 if (select(max+1, rset, wset, NULL, NULL) < 0) { 1074 if (errno == EINTR) 1075 continue; 1076 exit(2); 1077 } 1078 1079 /* copy stdin to iqueue */ 1080 if (FD_ISSET(in, rset)) { 1081 char buf[4*4096]; 1082 len = read(in, buf, sizeof buf); 1083 if (len == 0) { 1084 debug("read eof"); 1085 exit(0); 1086 } else if (len < 0) { 1087 error("read error"); 1088 exit(1); 1089 } else { 1090 buffer_append(&iqueue, buf, len); 1091 } 1092 } 1093 /* send oqueue to stdout */ 1094 if (FD_ISSET(out, wset)) { 1095 len = write(out, buffer_ptr(&oqueue), olen); 1096 if (len < 0) { 1097 error("write error"); 1098 exit(1); 1099 } else { 1100 buffer_consume(&oqueue, len); 1101 } 1102 } 1103 /* process requests from client */ 1104 process(); 1105 } 1106} 1107