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