1*4f52dfbbSDag-Erling Smørgrav /* $OpenBSD: sftp-server.c,v 1.111 2017/04/04 00:24:56 djm Exp $ */ 2b66f2d16SKris Kennaway /* 3efcad6b7SDag-Erling Smørgrav * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. 4b66f2d16SKris Kennaway * 5efcad6b7SDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any 6efcad6b7SDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above 7efcad6b7SDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies. 8b66f2d16SKris Kennaway * 9efcad6b7SDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10efcad6b7SDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11efcad6b7SDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12efcad6b7SDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13efcad6b7SDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14efcad6b7SDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15efcad6b7SDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16b66f2d16SKris Kennaway */ 17b66f2d16SKris Kennaway 18761efaa7SDag-Erling Smørgrav #include "includes.h" 19761efaa7SDag-Erling Smørgrav 20761efaa7SDag-Erling Smørgrav #include <sys/types.h> 21761efaa7SDag-Erling Smørgrav #include <sys/stat.h> 22761efaa7SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H 23761efaa7SDag-Erling Smørgrav # include <sys/time.h> 24761efaa7SDag-Erling Smørgrav #endif 25d4af9e69SDag-Erling Smørgrav #ifdef HAVE_SYS_MOUNT_H 26d4af9e69SDag-Erling Smørgrav #include <sys/mount.h> 27d4af9e69SDag-Erling Smørgrav #endif 28d4af9e69SDag-Erling Smørgrav #ifdef HAVE_SYS_STATVFS_H 29d4af9e69SDag-Erling Smørgrav #include <sys/statvfs.h> 30d4af9e69SDag-Erling Smørgrav #endif 31761efaa7SDag-Erling Smørgrav 32761efaa7SDag-Erling Smørgrav #include <dirent.h> 33761efaa7SDag-Erling Smørgrav #include <errno.h> 34761efaa7SDag-Erling Smørgrav #include <fcntl.h> 35761efaa7SDag-Erling Smørgrav #include <pwd.h> 36761efaa7SDag-Erling Smørgrav #include <stdlib.h> 37761efaa7SDag-Erling Smørgrav #include <stdio.h> 38761efaa7SDag-Erling Smørgrav #include <string.h> 39761efaa7SDag-Erling Smørgrav #include <time.h> 40761efaa7SDag-Erling Smørgrav #include <unistd.h> 41761efaa7SDag-Erling Smørgrav #include <stdarg.h> 42761efaa7SDag-Erling Smørgrav 43b66f2d16SKris Kennaway #include "xmalloc.h" 44bc5531deSDag-Erling Smørgrav #include "sshbuf.h" 45bc5531deSDag-Erling Smørgrav #include "ssherr.h" 46761efaa7SDag-Erling Smørgrav #include "log.h" 47021d409fSDag-Erling Smørgrav #include "misc.h" 48f7167e0eSDag-Erling Smørgrav #include "match.h" 49761efaa7SDag-Erling Smørgrav #include "uidswap.h" 50b66f2d16SKris Kennaway 511e8db6e2SBrian Feldman #include "sftp.h" 521e8db6e2SBrian Feldman #include "sftp-common.h" 53b66f2d16SKris Kennaway 54761efaa7SDag-Erling Smørgrav /* Our verbosity */ 55f7167e0eSDag-Erling Smørgrav static LogLevel log_level = SYSLOG_LEVEL_ERROR; 56761efaa7SDag-Erling Smørgrav 57761efaa7SDag-Erling Smørgrav /* Our client */ 58f7167e0eSDag-Erling Smørgrav static struct passwd *pw = NULL; 59f7167e0eSDag-Erling Smørgrav static char *client_addr = NULL; 6083d2307dSDag-Erling Smørgrav 61b66f2d16SKris Kennaway /* input and output queue */ 62bc5531deSDag-Erling Smørgrav struct sshbuf *iqueue; 63bc5531deSDag-Erling Smørgrav struct sshbuf *oqueue; 64b66f2d16SKris Kennaway 651e8db6e2SBrian Feldman /* Version of client */ 66f7167e0eSDag-Erling Smørgrav static u_int version; 67f7167e0eSDag-Erling Smørgrav 68f7167e0eSDag-Erling Smørgrav /* SSH2_FXP_INIT received */ 69f7167e0eSDag-Erling Smørgrav static int init_done; 701e8db6e2SBrian Feldman 71b15c8340SDag-Erling Smørgrav /* Disable writes */ 72f7167e0eSDag-Erling Smørgrav static int readonly; 73f7167e0eSDag-Erling Smørgrav 74f7167e0eSDag-Erling Smørgrav /* Requests that are allowed/denied */ 75f7167e0eSDag-Erling Smørgrav static char *request_whitelist, *request_blacklist; 76b15c8340SDag-Erling Smørgrav 77d95e11bfSDag-Erling Smørgrav /* portable attributes, etc. */ 78b66f2d16SKris Kennaway typedef struct Stat Stat; 79b66f2d16SKris Kennaway 801e8db6e2SBrian Feldman struct Stat { 81b66f2d16SKris Kennaway char *name; 82b66f2d16SKris Kennaway char *long_name; 83b66f2d16SKris Kennaway Attrib attrib; 84b66f2d16SKris Kennaway }; 85b66f2d16SKris Kennaway 86f7167e0eSDag-Erling Smørgrav /* Packet handlers */ 87f7167e0eSDag-Erling Smørgrav static void process_open(u_int32_t id); 88f7167e0eSDag-Erling Smørgrav static void process_close(u_int32_t id); 89f7167e0eSDag-Erling Smørgrav static void process_read(u_int32_t id); 90f7167e0eSDag-Erling Smørgrav static void process_write(u_int32_t id); 91f7167e0eSDag-Erling Smørgrav static void process_stat(u_int32_t id); 92f7167e0eSDag-Erling Smørgrav static void process_lstat(u_int32_t id); 93f7167e0eSDag-Erling Smørgrav static void process_fstat(u_int32_t id); 94f7167e0eSDag-Erling Smørgrav static void process_setstat(u_int32_t id); 95f7167e0eSDag-Erling Smørgrav static void process_fsetstat(u_int32_t id); 96f7167e0eSDag-Erling Smørgrav static void process_opendir(u_int32_t id); 97f7167e0eSDag-Erling Smørgrav static void process_readdir(u_int32_t id); 98f7167e0eSDag-Erling Smørgrav static void process_remove(u_int32_t id); 99f7167e0eSDag-Erling Smørgrav static void process_mkdir(u_int32_t id); 100f7167e0eSDag-Erling Smørgrav static void process_rmdir(u_int32_t id); 101f7167e0eSDag-Erling Smørgrav static void process_realpath(u_int32_t id); 102f7167e0eSDag-Erling Smørgrav static void process_rename(u_int32_t id); 103f7167e0eSDag-Erling Smørgrav static void process_readlink(u_int32_t id); 104f7167e0eSDag-Erling Smørgrav static void process_symlink(u_int32_t id); 105f7167e0eSDag-Erling Smørgrav static void process_extended_posix_rename(u_int32_t id); 106f7167e0eSDag-Erling Smørgrav static void process_extended_statvfs(u_int32_t id); 107f7167e0eSDag-Erling Smørgrav static void process_extended_fstatvfs(u_int32_t id); 108f7167e0eSDag-Erling Smørgrav static void process_extended_hardlink(u_int32_t id); 109f7167e0eSDag-Erling Smørgrav static void process_extended_fsync(u_int32_t id); 110f7167e0eSDag-Erling Smørgrav static void process_extended(u_int32_t id); 111f7167e0eSDag-Erling Smørgrav 112f7167e0eSDag-Erling Smørgrav struct sftp_handler { 113f7167e0eSDag-Erling Smørgrav const char *name; /* user-visible name for fine-grained perms */ 114f7167e0eSDag-Erling Smørgrav const char *ext_name; /* extended request name */ 115f7167e0eSDag-Erling Smørgrav u_int type; /* packet type, for non extended packets */ 116f7167e0eSDag-Erling Smørgrav void (*handler)(u_int32_t); 117f7167e0eSDag-Erling Smørgrav int does_write; /* if nonzero, banned for readonly mode */ 118f7167e0eSDag-Erling Smørgrav }; 119f7167e0eSDag-Erling Smørgrav 120f7167e0eSDag-Erling Smørgrav struct sftp_handler handlers[] = { 121f7167e0eSDag-Erling Smørgrav /* NB. SSH2_FXP_OPEN does the readonly check in the handler itself */ 122f7167e0eSDag-Erling Smørgrav { "open", NULL, SSH2_FXP_OPEN, process_open, 0 }, 123f7167e0eSDag-Erling Smørgrav { "close", NULL, SSH2_FXP_CLOSE, process_close, 0 }, 124f7167e0eSDag-Erling Smørgrav { "read", NULL, SSH2_FXP_READ, process_read, 0 }, 125f7167e0eSDag-Erling Smørgrav { "write", NULL, SSH2_FXP_WRITE, process_write, 1 }, 126f7167e0eSDag-Erling Smørgrav { "lstat", NULL, SSH2_FXP_LSTAT, process_lstat, 0 }, 127f7167e0eSDag-Erling Smørgrav { "fstat", NULL, SSH2_FXP_FSTAT, process_fstat, 0 }, 128f7167e0eSDag-Erling Smørgrav { "setstat", NULL, SSH2_FXP_SETSTAT, process_setstat, 1 }, 129f7167e0eSDag-Erling Smørgrav { "fsetstat", NULL, SSH2_FXP_FSETSTAT, process_fsetstat, 1 }, 130f7167e0eSDag-Erling Smørgrav { "opendir", NULL, SSH2_FXP_OPENDIR, process_opendir, 0 }, 131f7167e0eSDag-Erling Smørgrav { "readdir", NULL, SSH2_FXP_READDIR, process_readdir, 0 }, 132f7167e0eSDag-Erling Smørgrav { "remove", NULL, SSH2_FXP_REMOVE, process_remove, 1 }, 133f7167e0eSDag-Erling Smørgrav { "mkdir", NULL, SSH2_FXP_MKDIR, process_mkdir, 1 }, 134f7167e0eSDag-Erling Smørgrav { "rmdir", NULL, SSH2_FXP_RMDIR, process_rmdir, 1 }, 135f7167e0eSDag-Erling Smørgrav { "realpath", NULL, SSH2_FXP_REALPATH, process_realpath, 0 }, 136f7167e0eSDag-Erling Smørgrav { "stat", NULL, SSH2_FXP_STAT, process_stat, 0 }, 137f7167e0eSDag-Erling Smørgrav { "rename", NULL, SSH2_FXP_RENAME, process_rename, 1 }, 138f7167e0eSDag-Erling Smørgrav { "readlink", NULL, SSH2_FXP_READLINK, process_readlink, 0 }, 139f7167e0eSDag-Erling Smørgrav { "symlink", NULL, SSH2_FXP_SYMLINK, process_symlink, 1 }, 140f7167e0eSDag-Erling Smørgrav { NULL, NULL, 0, NULL, 0 } 141f7167e0eSDag-Erling Smørgrav }; 142f7167e0eSDag-Erling Smørgrav 143f7167e0eSDag-Erling Smørgrav /* SSH2_FXP_EXTENDED submessages */ 144f7167e0eSDag-Erling Smørgrav struct sftp_handler extended_handlers[] = { 145f7167e0eSDag-Erling Smørgrav { "posix-rename", "posix-rename@openssh.com", 0, 146f7167e0eSDag-Erling Smørgrav process_extended_posix_rename, 1 }, 147f7167e0eSDag-Erling Smørgrav { "statvfs", "statvfs@openssh.com", 0, process_extended_statvfs, 0 }, 148f7167e0eSDag-Erling Smørgrav { "fstatvfs", "fstatvfs@openssh.com", 0, process_extended_fstatvfs, 0 }, 149f7167e0eSDag-Erling Smørgrav { "hardlink", "hardlink@openssh.com", 0, process_extended_hardlink, 1 }, 150f7167e0eSDag-Erling Smørgrav { "fsync", "fsync@openssh.com", 0, process_extended_fsync, 1 }, 151f7167e0eSDag-Erling Smørgrav { NULL, NULL, 0, NULL, 0 } 152f7167e0eSDag-Erling Smørgrav }; 153f7167e0eSDag-Erling Smørgrav 154f7167e0eSDag-Erling Smørgrav static int 155f7167e0eSDag-Erling Smørgrav request_permitted(struct sftp_handler *h) 156f7167e0eSDag-Erling Smørgrav { 157f7167e0eSDag-Erling Smørgrav char *result; 158f7167e0eSDag-Erling Smørgrav 159f7167e0eSDag-Erling Smørgrav if (readonly && h->does_write) { 160f7167e0eSDag-Erling Smørgrav verbose("Refusing %s request in read-only mode", h->name); 161f7167e0eSDag-Erling Smørgrav return 0; 162f7167e0eSDag-Erling Smørgrav } 163f7167e0eSDag-Erling Smørgrav if (request_blacklist != NULL && 164f7167e0eSDag-Erling Smørgrav ((result = match_list(h->name, request_blacklist, NULL))) != NULL) { 165f7167e0eSDag-Erling Smørgrav free(result); 166f7167e0eSDag-Erling Smørgrav verbose("Refusing blacklisted %s request", h->name); 167f7167e0eSDag-Erling Smørgrav return 0; 168f7167e0eSDag-Erling Smørgrav } 169f7167e0eSDag-Erling Smørgrav if (request_whitelist != NULL && 170f7167e0eSDag-Erling Smørgrav ((result = match_list(h->name, request_whitelist, NULL))) != NULL) { 171f7167e0eSDag-Erling Smørgrav free(result); 172f7167e0eSDag-Erling Smørgrav debug2("Permitting whitelisted %s request", h->name); 173f7167e0eSDag-Erling Smørgrav return 1; 174f7167e0eSDag-Erling Smørgrav } 175f7167e0eSDag-Erling Smørgrav if (request_whitelist != NULL) { 176f7167e0eSDag-Erling Smørgrav verbose("Refusing non-whitelisted %s request", h->name); 177f7167e0eSDag-Erling Smørgrav return 0; 178f7167e0eSDag-Erling Smørgrav } 179f7167e0eSDag-Erling Smørgrav return 1; 180f7167e0eSDag-Erling Smørgrav } 181f7167e0eSDag-Erling Smørgrav 182ae1f160dSDag-Erling Smørgrav static int 183b66f2d16SKris Kennaway errno_to_portable(int unixerrno) 184b66f2d16SKris Kennaway { 185b66f2d16SKris Kennaway int ret = 0; 1861e8db6e2SBrian Feldman 187b66f2d16SKris Kennaway switch (unixerrno) { 188b66f2d16SKris Kennaway case 0: 1891e8db6e2SBrian Feldman ret = SSH2_FX_OK; 190b66f2d16SKris Kennaway break; 191b66f2d16SKris Kennaway case ENOENT: 192b66f2d16SKris Kennaway case ENOTDIR: 193b66f2d16SKris Kennaway case EBADF: 194b66f2d16SKris Kennaway case ELOOP: 1951e8db6e2SBrian Feldman ret = SSH2_FX_NO_SUCH_FILE; 196b66f2d16SKris Kennaway break; 197b66f2d16SKris Kennaway case EPERM: 198b66f2d16SKris Kennaway case EACCES: 199b66f2d16SKris Kennaway case EFAULT: 2001e8db6e2SBrian Feldman ret = SSH2_FX_PERMISSION_DENIED; 201b66f2d16SKris Kennaway break; 202b66f2d16SKris Kennaway case ENAMETOOLONG: 203b66f2d16SKris Kennaway case EINVAL: 2041e8db6e2SBrian Feldman ret = SSH2_FX_BAD_MESSAGE; 205b66f2d16SKris Kennaway break; 206d4af9e69SDag-Erling Smørgrav case ENOSYS: 207d4af9e69SDag-Erling Smørgrav ret = SSH2_FX_OP_UNSUPPORTED; 208d4af9e69SDag-Erling Smørgrav break; 209b66f2d16SKris Kennaway default: 2101e8db6e2SBrian Feldman ret = SSH2_FX_FAILURE; 211b66f2d16SKris Kennaway break; 212b66f2d16SKris Kennaway } 213b66f2d16SKris Kennaway return ret; 214b66f2d16SKris Kennaway } 215b66f2d16SKris Kennaway 216ae1f160dSDag-Erling Smørgrav static int 217b66f2d16SKris Kennaway flags_from_portable(int pflags) 218b66f2d16SKris Kennaway { 219b66f2d16SKris Kennaway int flags = 0; 2201e8db6e2SBrian Feldman 2211e8db6e2SBrian Feldman if ((pflags & SSH2_FXF_READ) && 2221e8db6e2SBrian Feldman (pflags & SSH2_FXF_WRITE)) { 223b66f2d16SKris Kennaway flags = O_RDWR; 2241e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_READ) { 225b66f2d16SKris Kennaway flags = O_RDONLY; 2261e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_WRITE) { 227b66f2d16SKris Kennaway flags = O_WRONLY; 228b66f2d16SKris Kennaway } 229f7167e0eSDag-Erling Smørgrav if (pflags & SSH2_FXF_APPEND) 230f7167e0eSDag-Erling Smørgrav flags |= O_APPEND; 2311e8db6e2SBrian Feldman if (pflags & SSH2_FXF_CREAT) 232b66f2d16SKris Kennaway flags |= O_CREAT; 2331e8db6e2SBrian Feldman if (pflags & SSH2_FXF_TRUNC) 234b66f2d16SKris Kennaway flags |= O_TRUNC; 2351e8db6e2SBrian Feldman if (pflags & SSH2_FXF_EXCL) 236b66f2d16SKris Kennaway flags |= O_EXCL; 237b66f2d16SKris Kennaway return flags; 238b66f2d16SKris Kennaway } 239b66f2d16SKris Kennaway 240761efaa7SDag-Erling Smørgrav static const char * 241761efaa7SDag-Erling Smørgrav string_from_portable(int pflags) 242761efaa7SDag-Erling Smørgrav { 243761efaa7SDag-Erling Smørgrav static char ret[128]; 244761efaa7SDag-Erling Smørgrav 245761efaa7SDag-Erling Smørgrav *ret = '\0'; 246761efaa7SDag-Erling Smørgrav 247761efaa7SDag-Erling Smørgrav #define PAPPEND(str) { \ 248761efaa7SDag-Erling Smørgrav if (*ret != '\0') \ 249761efaa7SDag-Erling Smørgrav strlcat(ret, ",", sizeof(ret)); \ 250761efaa7SDag-Erling Smørgrav strlcat(ret, str, sizeof(ret)); \ 251761efaa7SDag-Erling Smørgrav } 252761efaa7SDag-Erling Smørgrav 253761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_READ) 254761efaa7SDag-Erling Smørgrav PAPPEND("READ") 255761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_WRITE) 256761efaa7SDag-Erling Smørgrav PAPPEND("WRITE") 257f7167e0eSDag-Erling Smørgrav if (pflags & SSH2_FXF_APPEND) 258f7167e0eSDag-Erling Smørgrav PAPPEND("APPEND") 259761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_CREAT) 260761efaa7SDag-Erling Smørgrav PAPPEND("CREATE") 261761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_TRUNC) 262761efaa7SDag-Erling Smørgrav PAPPEND("TRUNCATE") 263761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_EXCL) 264761efaa7SDag-Erling Smørgrav PAPPEND("EXCL") 265761efaa7SDag-Erling Smørgrav 266761efaa7SDag-Erling Smørgrav return ret; 267761efaa7SDag-Erling Smørgrav } 268761efaa7SDag-Erling Smørgrav 269b66f2d16SKris Kennaway /* handle handles */ 270b66f2d16SKris Kennaway 271b66f2d16SKris Kennaway typedef struct Handle Handle; 272b66f2d16SKris Kennaway struct Handle { 273b66f2d16SKris Kennaway int use; 274b66f2d16SKris Kennaway DIR *dirp; 275b66f2d16SKris Kennaway int fd; 276f7167e0eSDag-Erling Smørgrav int flags; 277b66f2d16SKris Kennaway char *name; 278761efaa7SDag-Erling Smørgrav u_int64_t bytes_read, bytes_write; 279d4af9e69SDag-Erling Smørgrav int next_unused; 280b66f2d16SKris Kennaway }; 2811e8db6e2SBrian Feldman 282b66f2d16SKris Kennaway enum { 283b66f2d16SKris Kennaway HANDLE_UNUSED, 284b66f2d16SKris Kennaway HANDLE_DIR, 285b66f2d16SKris Kennaway HANDLE_FILE 286b66f2d16SKris Kennaway }; 2871e8db6e2SBrian Feldman 288d4af9e69SDag-Erling Smørgrav Handle *handles = NULL; 289d4af9e69SDag-Erling Smørgrav u_int num_handles = 0; 290d4af9e69SDag-Erling Smørgrav int first_unused_handle = -1; 291b66f2d16SKris Kennaway 292d4af9e69SDag-Erling Smørgrav static void handle_unused(int i) 293b66f2d16SKris Kennaway { 294b66f2d16SKris Kennaway handles[i].use = HANDLE_UNUSED; 295d4af9e69SDag-Erling Smørgrav handles[i].next_unused = first_unused_handle; 296d4af9e69SDag-Erling Smørgrav first_unused_handle = i; 297b66f2d16SKris Kennaway } 298b66f2d16SKris Kennaway 299ae1f160dSDag-Erling Smørgrav static int 300f7167e0eSDag-Erling Smørgrav handle_new(int use, const char *name, int fd, int flags, DIR *dirp) 301b66f2d16SKris Kennaway { 302d4af9e69SDag-Erling Smørgrav int i; 3031e8db6e2SBrian Feldman 304d4af9e69SDag-Erling Smørgrav if (first_unused_handle == -1) { 305d4af9e69SDag-Erling Smørgrav if (num_handles + 1 <= num_handles) 306d4af9e69SDag-Erling Smørgrav return -1; 307d4af9e69SDag-Erling Smørgrav num_handles++; 308557f75e5SDag-Erling Smørgrav handles = xreallocarray(handles, num_handles, sizeof(Handle)); 309d4af9e69SDag-Erling Smørgrav handle_unused(num_handles - 1); 310d4af9e69SDag-Erling Smørgrav } 311d4af9e69SDag-Erling Smørgrav 312d4af9e69SDag-Erling Smørgrav i = first_unused_handle; 313d4af9e69SDag-Erling Smørgrav first_unused_handle = handles[i].next_unused; 314d4af9e69SDag-Erling Smørgrav 315b66f2d16SKris Kennaway handles[i].use = use; 316b66f2d16SKris Kennaway handles[i].dirp = dirp; 317b66f2d16SKris Kennaway handles[i].fd = fd; 318f7167e0eSDag-Erling Smørgrav handles[i].flags = flags; 319d0c8c0bcSDag-Erling Smørgrav handles[i].name = xstrdup(name); 320761efaa7SDag-Erling Smørgrav handles[i].bytes_read = handles[i].bytes_write = 0; 321d4af9e69SDag-Erling Smørgrav 322b66f2d16SKris Kennaway return i; 323b66f2d16SKris Kennaway } 324b66f2d16SKris Kennaway 325ae1f160dSDag-Erling Smørgrav static int 326b66f2d16SKris Kennaway handle_is_ok(int i, int type) 327b66f2d16SKris Kennaway { 328d4af9e69SDag-Erling Smørgrav return i >= 0 && (u_int)i < num_handles && handles[i].use == type; 329b66f2d16SKris Kennaway } 330b66f2d16SKris Kennaway 331ae1f160dSDag-Erling Smørgrav static int 332bc5531deSDag-Erling Smørgrav handle_to_string(int handle, u_char **stringp, int *hlenp) 333b66f2d16SKris Kennaway { 334b66f2d16SKris Kennaway if (stringp == NULL || hlenp == NULL) 335b66f2d16SKris Kennaway return -1; 3361e8db6e2SBrian Feldman *stringp = xmalloc(sizeof(int32_t)); 337761efaa7SDag-Erling Smørgrav put_u32(*stringp, handle); 3381e8db6e2SBrian Feldman *hlenp = sizeof(int32_t); 339b66f2d16SKris Kennaway return 0; 340b66f2d16SKris Kennaway } 341b66f2d16SKris Kennaway 342ae1f160dSDag-Erling Smørgrav static int 343bc5531deSDag-Erling Smørgrav handle_from_string(const u_char *handle, u_int hlen) 344b66f2d16SKris Kennaway { 3451e8db6e2SBrian Feldman int val; 3461e8db6e2SBrian Feldman 3471e8db6e2SBrian Feldman if (hlen != sizeof(int32_t)) 348b66f2d16SKris Kennaway return -1; 349761efaa7SDag-Erling Smørgrav val = get_u32(handle); 350b66f2d16SKris Kennaway if (handle_is_ok(val, HANDLE_FILE) || 351b66f2d16SKris Kennaway handle_is_ok(val, HANDLE_DIR)) 352b66f2d16SKris Kennaway return val; 353b66f2d16SKris Kennaway return -1; 354b66f2d16SKris Kennaway } 355b66f2d16SKris Kennaway 356ae1f160dSDag-Erling Smørgrav static char * 357b66f2d16SKris Kennaway handle_to_name(int handle) 358b66f2d16SKris Kennaway { 359b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)|| 360b66f2d16SKris Kennaway handle_is_ok(handle, HANDLE_FILE)) 361b66f2d16SKris Kennaway return handles[handle].name; 362b66f2d16SKris Kennaway return NULL; 363b66f2d16SKris Kennaway } 364b66f2d16SKris Kennaway 365ae1f160dSDag-Erling Smørgrav static DIR * 366b66f2d16SKris Kennaway handle_to_dir(int handle) 367b66f2d16SKris Kennaway { 368b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)) 369b66f2d16SKris Kennaway return handles[handle].dirp; 370b66f2d16SKris Kennaway return NULL; 371b66f2d16SKris Kennaway } 372b66f2d16SKris Kennaway 373ae1f160dSDag-Erling Smørgrav static int 374b66f2d16SKris Kennaway handle_to_fd(int handle) 375b66f2d16SKris Kennaway { 376b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) 377b66f2d16SKris Kennaway return handles[handle].fd; 378b66f2d16SKris Kennaway return -1; 379b66f2d16SKris Kennaway } 380b66f2d16SKris Kennaway 381f7167e0eSDag-Erling Smørgrav static int 382f7167e0eSDag-Erling Smørgrav handle_to_flags(int handle) 383f7167e0eSDag-Erling Smørgrav { 384f7167e0eSDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 385f7167e0eSDag-Erling Smørgrav return handles[handle].flags; 386f7167e0eSDag-Erling Smørgrav return 0; 387f7167e0eSDag-Erling Smørgrav } 388f7167e0eSDag-Erling Smørgrav 389761efaa7SDag-Erling Smørgrav static void 390761efaa7SDag-Erling Smørgrav handle_update_read(int handle, ssize_t bytes) 391761efaa7SDag-Erling Smørgrav { 392761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 393761efaa7SDag-Erling Smørgrav handles[handle].bytes_read += bytes; 394761efaa7SDag-Erling Smørgrav } 395761efaa7SDag-Erling Smørgrav 396761efaa7SDag-Erling Smørgrav static void 397761efaa7SDag-Erling Smørgrav handle_update_write(int handle, ssize_t bytes) 398761efaa7SDag-Erling Smørgrav { 399761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 400761efaa7SDag-Erling Smørgrav handles[handle].bytes_write += bytes; 401761efaa7SDag-Erling Smørgrav } 402761efaa7SDag-Erling Smørgrav 403761efaa7SDag-Erling Smørgrav static u_int64_t 404761efaa7SDag-Erling Smørgrav handle_bytes_read(int handle) 405761efaa7SDag-Erling Smørgrav { 406761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 407761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_read); 408761efaa7SDag-Erling Smørgrav return 0; 409761efaa7SDag-Erling Smørgrav } 410761efaa7SDag-Erling Smørgrav 411761efaa7SDag-Erling Smørgrav static u_int64_t 412761efaa7SDag-Erling Smørgrav handle_bytes_write(int handle) 413761efaa7SDag-Erling Smørgrav { 414761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 415761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_write); 416761efaa7SDag-Erling Smørgrav return 0; 417761efaa7SDag-Erling Smørgrav } 418761efaa7SDag-Erling Smørgrav 419ae1f160dSDag-Erling Smørgrav static int 420b66f2d16SKris Kennaway handle_close(int handle) 421b66f2d16SKris Kennaway { 422b66f2d16SKris Kennaway int ret = -1; 4231e8db6e2SBrian Feldman 424b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) { 425b66f2d16SKris Kennaway ret = close(handles[handle].fd); 426e4a9863fSDag-Erling Smørgrav free(handles[handle].name); 427d4af9e69SDag-Erling Smørgrav handle_unused(handle); 428b66f2d16SKris Kennaway } else if (handle_is_ok(handle, HANDLE_DIR)) { 429b66f2d16SKris Kennaway ret = closedir(handles[handle].dirp); 430e4a9863fSDag-Erling Smørgrav free(handles[handle].name); 431d4af9e69SDag-Erling Smørgrav handle_unused(handle); 432b66f2d16SKris Kennaway } else { 433b66f2d16SKris Kennaway errno = ENOENT; 434b66f2d16SKris Kennaway } 435b66f2d16SKris Kennaway return ret; 436b66f2d16SKris Kennaway } 437b66f2d16SKris Kennaway 438761efaa7SDag-Erling Smørgrav static void 439761efaa7SDag-Erling Smørgrav handle_log_close(int handle, char *emsg) 440761efaa7SDag-Erling Smørgrav { 441761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) { 442761efaa7SDag-Erling Smørgrav logit("%s%sclose \"%s\" bytes read %llu written %llu", 443761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 444761efaa7SDag-Erling Smørgrav handle_to_name(handle), 445d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_read(handle), 446d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_write(handle)); 447761efaa7SDag-Erling Smørgrav } else { 448761efaa7SDag-Erling Smørgrav logit("%s%sclosedir \"%s\"", 449761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 450761efaa7SDag-Erling Smørgrav handle_to_name(handle)); 451761efaa7SDag-Erling Smørgrav } 452761efaa7SDag-Erling Smørgrav } 453761efaa7SDag-Erling Smørgrav 454761efaa7SDag-Erling Smørgrav static void 455761efaa7SDag-Erling Smørgrav handle_log_exit(void) 456761efaa7SDag-Erling Smørgrav { 457761efaa7SDag-Erling Smørgrav u_int i; 458761efaa7SDag-Erling Smørgrav 459d4af9e69SDag-Erling Smørgrav for (i = 0; i < num_handles; i++) 460761efaa7SDag-Erling Smørgrav if (handles[i].use != HANDLE_UNUSED) 461761efaa7SDag-Erling Smørgrav handle_log_close(i, "forced"); 462761efaa7SDag-Erling Smørgrav } 463761efaa7SDag-Erling Smørgrav 464ae1f160dSDag-Erling Smørgrav static int 465bc5531deSDag-Erling Smørgrav get_handle(struct sshbuf *queue, int *hp) 466b66f2d16SKris Kennaway { 467bc5531deSDag-Erling Smørgrav u_char *handle; 468bc5531deSDag-Erling Smørgrav int r; 469bc5531deSDag-Erling Smørgrav size_t hlen; 4701e8db6e2SBrian Feldman 471bc5531deSDag-Erling Smørgrav *hp = -1; 472bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_string(queue, &handle, &hlen)) != 0) 473bc5531deSDag-Erling Smørgrav return r; 4741e8db6e2SBrian Feldman if (hlen < 256) 475bc5531deSDag-Erling Smørgrav *hp = handle_from_string(handle, hlen); 476e4a9863fSDag-Erling Smørgrav free(handle); 477bc5531deSDag-Erling Smørgrav return 0; 478b66f2d16SKris Kennaway } 479b66f2d16SKris Kennaway 480b66f2d16SKris Kennaway /* send replies */ 481b66f2d16SKris Kennaway 482ae1f160dSDag-Erling Smørgrav static void 483bc5531deSDag-Erling Smørgrav send_msg(struct sshbuf *m) 484b66f2d16SKris Kennaway { 485bc5531deSDag-Erling Smørgrav int r; 4861e8db6e2SBrian Feldman 487bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_stringb(oqueue, m)) != 0) 488bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 489bc5531deSDag-Erling Smørgrav sshbuf_reset(m); 490b66f2d16SKris Kennaway } 491b66f2d16SKris Kennaway 492761efaa7SDag-Erling Smørgrav static const char * 493761efaa7SDag-Erling Smørgrav status_to_message(u_int32_t status) 494b66f2d16SKris Kennaway { 4951e8db6e2SBrian Feldman const char *status_messages[] = { 4961e8db6e2SBrian Feldman "Success", /* SSH_FX_OK */ 4971e8db6e2SBrian Feldman "End of file", /* SSH_FX_EOF */ 4981e8db6e2SBrian Feldman "No such file", /* SSH_FX_NO_SUCH_FILE */ 4991e8db6e2SBrian Feldman "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 5001e8db6e2SBrian Feldman "Failure", /* SSH_FX_FAILURE */ 5011e8db6e2SBrian Feldman "Bad message", /* SSH_FX_BAD_MESSAGE */ 5021e8db6e2SBrian Feldman "No connection", /* SSH_FX_NO_CONNECTION */ 5031e8db6e2SBrian Feldman "Connection lost", /* SSH_FX_CONNECTION_LOST */ 5041e8db6e2SBrian Feldman "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 5051e8db6e2SBrian Feldman "Unknown error" /* Others */ 5061e8db6e2SBrian Feldman }; 507ca86bcf2SDag-Erling Smørgrav return (status_messages[MINIMUM(status,SSH2_FX_MAX)]); 508761efaa7SDag-Erling Smørgrav } 5091e8db6e2SBrian Feldman 510761efaa7SDag-Erling Smørgrav static void 511761efaa7SDag-Erling Smørgrav send_status(u_int32_t id, u_int32_t status) 512761efaa7SDag-Erling Smørgrav { 513bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 514bc5531deSDag-Erling Smørgrav int r; 515761efaa7SDag-Erling Smørgrav 516761efaa7SDag-Erling Smørgrav debug3("request %u: sent status %u", id, status); 517761efaa7SDag-Erling Smørgrav if (log_level > SYSLOG_LEVEL_VERBOSE || 518761efaa7SDag-Erling Smørgrav (status != SSH2_FX_OK && status != SSH2_FX_EOF)) 519761efaa7SDag-Erling Smørgrav logit("sent status %s", status_to_message(status)); 520bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 521bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 522bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_STATUS)) != 0 || 523bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 524bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, status)) != 0) 525bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 5261e8db6e2SBrian Feldman if (version >= 3) { 527bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(msg, 528bc5531deSDag-Erling Smørgrav status_to_message(status))) != 0 || 529bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "")) != 0) 530bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 5311e8db6e2SBrian Feldman } 532bc5531deSDag-Erling Smørgrav send_msg(msg); 533bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 534b66f2d16SKris Kennaway } 535ae1f160dSDag-Erling Smørgrav static void 536bc5531deSDag-Erling Smørgrav send_data_or_handle(char type, u_int32_t id, const u_char *data, int dlen) 537b66f2d16SKris Kennaway { 538bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 539bc5531deSDag-Erling Smørgrav int r; 5401e8db6e2SBrian Feldman 541bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 542bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 543bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, type)) != 0 || 544bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 545bc5531deSDag-Erling Smørgrav (r = sshbuf_put_string(msg, data, dlen)) != 0) 546bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 547bc5531deSDag-Erling Smørgrav send_msg(msg); 548bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 549b66f2d16SKris Kennaway } 550b66f2d16SKris Kennaway 551ae1f160dSDag-Erling Smørgrav static void 552bc5531deSDag-Erling Smørgrav send_data(u_int32_t id, const u_char *data, int dlen) 553b66f2d16SKris Kennaway { 554761efaa7SDag-Erling Smørgrav debug("request %u: sent data len %d", id, dlen); 5551e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 556b66f2d16SKris Kennaway } 557b66f2d16SKris Kennaway 558ae1f160dSDag-Erling Smørgrav static void 559b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle) 560b66f2d16SKris Kennaway { 561bc5531deSDag-Erling Smørgrav u_char *string; 562b66f2d16SKris Kennaway int hlen; 5631e8db6e2SBrian Feldman 564b66f2d16SKris Kennaway handle_to_string(handle, &string, &hlen); 565761efaa7SDag-Erling Smørgrav debug("request %u: sent handle handle %d", id, handle); 5661e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 567e4a9863fSDag-Erling Smørgrav free(string); 568b66f2d16SKris Kennaway } 569b66f2d16SKris Kennaway 570ae1f160dSDag-Erling Smørgrav static void 571efcad6b7SDag-Erling Smørgrav send_names(u_int32_t id, int count, const Stat *stats) 572b66f2d16SKris Kennaway { 573bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 574bc5531deSDag-Erling Smørgrav int i, r; 5751e8db6e2SBrian Feldman 576bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 577bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 578bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_NAME)) != 0 || 579bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 580bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, count)) != 0) 581bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 582761efaa7SDag-Erling Smørgrav debug("request %u: sent names count %d", id, count); 583b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 584bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(msg, stats[i].name)) != 0 || 585bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, stats[i].long_name)) != 0 || 586bc5531deSDag-Erling Smørgrav (r = encode_attrib(msg, &stats[i].attrib)) != 0) 587bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 588b66f2d16SKris Kennaway } 589bc5531deSDag-Erling Smørgrav send_msg(msg); 590bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 591b66f2d16SKris Kennaway } 592b66f2d16SKris Kennaway 593ae1f160dSDag-Erling Smørgrav static void 594efcad6b7SDag-Erling Smørgrav send_attrib(u_int32_t id, const Attrib *a) 595b66f2d16SKris Kennaway { 596bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 597bc5531deSDag-Erling Smørgrav int r; 5981e8db6e2SBrian Feldman 599761efaa7SDag-Erling Smørgrav debug("request %u: sent attrib have 0x%x", id, a->flags); 600bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 601bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 602bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_ATTRS)) != 0 || 603bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 604bc5531deSDag-Erling Smørgrav (r = encode_attrib(msg, a)) != 0) 605bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 606bc5531deSDag-Erling Smørgrav send_msg(msg); 607bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 608b66f2d16SKris Kennaway } 609b66f2d16SKris Kennaway 610d4af9e69SDag-Erling Smørgrav static void 611d4af9e69SDag-Erling Smørgrav send_statvfs(u_int32_t id, struct statvfs *st) 612d4af9e69SDag-Erling Smørgrav { 613bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 614d4af9e69SDag-Erling Smørgrav u_int64_t flag; 615bc5531deSDag-Erling Smørgrav int r; 616d4af9e69SDag-Erling Smørgrav 617d4af9e69SDag-Erling Smørgrav flag = (st->f_flag & ST_RDONLY) ? SSH2_FXE_STATVFS_ST_RDONLY : 0; 618d4af9e69SDag-Erling Smørgrav flag |= (st->f_flag & ST_NOSUID) ? SSH2_FXE_STATVFS_ST_NOSUID : 0; 619d4af9e69SDag-Erling Smørgrav 620bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 621bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 622bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED_REPLY)) != 0 || 623bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 624bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_bsize)) != 0 || 625bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_frsize)) != 0 || 626bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_blocks)) != 0 || 627bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_bfree)) != 0 || 628bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_bavail)) != 0 || 629bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_files)) != 0 || 630bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_ffree)) != 0 || 631bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_favail)) != 0 || 632bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, FSID_TO_ULONG(st->f_fsid))) != 0 || 633bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, flag)) != 0 || 634bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_namemax)) != 0) 635bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 636bc5531deSDag-Erling Smørgrav send_msg(msg); 637bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 638d4af9e69SDag-Erling Smørgrav } 639d4af9e69SDag-Erling Smørgrav 640b66f2d16SKris Kennaway /* parse incoming */ 641b66f2d16SKris Kennaway 642ae1f160dSDag-Erling Smørgrav static void 643b66f2d16SKris Kennaway process_init(void) 644b66f2d16SKris Kennaway { 645bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 646bc5531deSDag-Erling Smørgrav int r; 647b66f2d16SKris Kennaway 648bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(iqueue, &version)) != 0) 649bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 650e146993eSDag-Erling Smørgrav verbose("received client version %u", version); 651bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 652bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 653bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_VERSION)) != 0 || 654bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, SSH2_FILEXFER_VERSION)) != 0 || 655d4af9e69SDag-Erling Smørgrav /* POSIX rename extension */ 656bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "posix-rename@openssh.com")) != 0 || 657bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "1")) != 0 || /* version */ 658d4af9e69SDag-Erling Smørgrav /* statvfs extension */ 659bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "statvfs@openssh.com")) != 0 || 660bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "2")) != 0 || /* version */ 661d4af9e69SDag-Erling Smørgrav /* fstatvfs extension */ 662bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "fstatvfs@openssh.com")) != 0 || 663bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "2")) != 0 || /* version */ 6644a421b63SDag-Erling Smørgrav /* hardlink extension */ 665bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "hardlink@openssh.com")) != 0 || 666bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "1")) != 0 || /* version */ 667f7167e0eSDag-Erling Smørgrav /* fsync extension */ 668bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "fsync@openssh.com")) != 0 || 669bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "1")) != 0) /* version */ 670bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 671bc5531deSDag-Erling Smørgrav send_msg(msg); 672bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 673b66f2d16SKris Kennaway } 674b66f2d16SKris Kennaway 675ae1f160dSDag-Erling Smørgrav static void 676f7167e0eSDag-Erling Smørgrav process_open(u_int32_t id) 677b66f2d16SKris Kennaway { 678f7167e0eSDag-Erling Smørgrav u_int32_t pflags; 679bc5531deSDag-Erling Smørgrav Attrib a; 680b66f2d16SKris Kennaway char *name; 681bc5531deSDag-Erling Smørgrav int r, handle, fd, flags, mode, status = SSH2_FX_FAILURE; 682b66f2d16SKris Kennaway 683bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 684bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u32(iqueue, &pflags)) != 0 || /* portable flags */ 685bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 686bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 687bc5531deSDag-Erling Smørgrav 688761efaa7SDag-Erling Smørgrav debug3("request %u: open flags %d", id, pflags); 689b66f2d16SKris Kennaway flags = flags_from_portable(pflags); 690bc5531deSDag-Erling Smørgrav mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a.perm : 0666; 691761efaa7SDag-Erling Smørgrav logit("open \"%s\" flags %s mode 0%o", 692761efaa7SDag-Erling Smørgrav name, string_from_portable(pflags), mode); 693b15c8340SDag-Erling Smørgrav if (readonly && 694*4f52dfbbSDag-Erling Smørgrav ((flags & O_ACCMODE) != O_RDONLY || 695*4f52dfbbSDag-Erling Smørgrav (flags & (O_CREAT|O_TRUNC)) != 0)) { 696f7167e0eSDag-Erling Smørgrav verbose("Refusing open request in read-only mode"); 697b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 698f7167e0eSDag-Erling Smørgrav } else { 699b66f2d16SKris Kennaway fd = open(name, flags, mode); 700b66f2d16SKris Kennaway if (fd < 0) { 701b66f2d16SKris Kennaway status = errno_to_portable(errno); 702b66f2d16SKris Kennaway } else { 703f7167e0eSDag-Erling Smørgrav handle = handle_new(HANDLE_FILE, name, fd, flags, NULL); 704b66f2d16SKris Kennaway if (handle < 0) { 705b66f2d16SKris Kennaway close(fd); 706b66f2d16SKris Kennaway } else { 707b66f2d16SKris Kennaway send_handle(id, handle); 7081e8db6e2SBrian Feldman status = SSH2_FX_OK; 709b66f2d16SKris Kennaway } 710b66f2d16SKris Kennaway } 711b15c8340SDag-Erling Smørgrav } 7121e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 713b66f2d16SKris Kennaway send_status(id, status); 714e4a9863fSDag-Erling Smørgrav free(name); 715b66f2d16SKris Kennaway } 716b66f2d16SKris Kennaway 717ae1f160dSDag-Erling Smørgrav static void 718f7167e0eSDag-Erling Smørgrav process_close(u_int32_t id) 719b66f2d16SKris Kennaway { 720bc5531deSDag-Erling Smørgrav int r, handle, ret, status = SSH2_FX_FAILURE; 721b66f2d16SKris Kennaway 722bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 723bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 724bc5531deSDag-Erling Smørgrav 725761efaa7SDag-Erling Smørgrav debug3("request %u: close handle %u", id, handle); 726761efaa7SDag-Erling Smørgrav handle_log_close(handle, NULL); 727b66f2d16SKris Kennaway ret = handle_close(handle); 7281e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 729b66f2d16SKris Kennaway send_status(id, status); 730b66f2d16SKris Kennaway } 731b66f2d16SKris Kennaway 732ae1f160dSDag-Erling Smørgrav static void 733f7167e0eSDag-Erling Smørgrav process_read(u_int32_t id) 734b66f2d16SKris Kennaway { 735bc5531deSDag-Erling Smørgrav u_char buf[64*1024]; 736f7167e0eSDag-Erling Smørgrav u_int32_t len; 737bc5531deSDag-Erling Smørgrav int r, handle, fd, ret, status = SSH2_FX_FAILURE; 738b66f2d16SKris Kennaway u_int64_t off; 739b66f2d16SKris Kennaway 740bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0 || 741bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u64(iqueue, &off)) != 0 || 742bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u32(iqueue, &len)) != 0) 743bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 744b66f2d16SKris Kennaway 745761efaa7SDag-Erling Smørgrav debug("request %u: read \"%s\" (handle %d) off %llu len %d", 746761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len); 747b66f2d16SKris Kennaway if (len > sizeof buf) { 748b66f2d16SKris Kennaway len = sizeof buf; 749761efaa7SDag-Erling Smørgrav debug2("read change len %d", len); 750b66f2d16SKris Kennaway } 751b66f2d16SKris Kennaway fd = handle_to_fd(handle); 752b66f2d16SKris Kennaway if (fd >= 0) { 753b66f2d16SKris Kennaway if (lseek(fd, off, SEEK_SET) < 0) { 754b66f2d16SKris Kennaway error("process_read: seek failed"); 755b66f2d16SKris Kennaway status = errno_to_portable(errno); 756b66f2d16SKris Kennaway } else { 757b66f2d16SKris Kennaway ret = read(fd, buf, len); 758b66f2d16SKris Kennaway if (ret < 0) { 759b66f2d16SKris Kennaway status = errno_to_portable(errno); 760b66f2d16SKris Kennaway } else if (ret == 0) { 7611e8db6e2SBrian Feldman status = SSH2_FX_EOF; 762b66f2d16SKris Kennaway } else { 763b66f2d16SKris Kennaway send_data(id, buf, ret); 7641e8db6e2SBrian Feldman status = SSH2_FX_OK; 765761efaa7SDag-Erling Smørgrav handle_update_read(handle, ret); 766b66f2d16SKris Kennaway } 767b66f2d16SKris Kennaway } 768b66f2d16SKris Kennaway } 7691e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 770b66f2d16SKris Kennaway send_status(id, status); 771b66f2d16SKris Kennaway } 772b66f2d16SKris Kennaway 773ae1f160dSDag-Erling Smørgrav static void 774f7167e0eSDag-Erling Smørgrav process_write(u_int32_t id) 775b66f2d16SKris Kennaway { 776b66f2d16SKris Kennaway u_int64_t off; 777bc5531deSDag-Erling Smørgrav size_t len; 778bc5531deSDag-Erling Smørgrav int r, handle, fd, ret, status; 779bc5531deSDag-Erling Smørgrav u_char *data; 780b66f2d16SKris Kennaway 781bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0 || 782bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u64(iqueue, &off)) != 0 || 783bc5531deSDag-Erling Smørgrav (r = sshbuf_get_string(iqueue, &data, &len)) != 0) 784bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 785b66f2d16SKris Kennaway 786bc5531deSDag-Erling Smørgrav debug("request %u: write \"%s\" (handle %d) off %llu len %zu", 787761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len); 788b66f2d16SKris Kennaway fd = handle_to_fd(handle); 789b15c8340SDag-Erling Smørgrav 790b15c8340SDag-Erling Smørgrav if (fd < 0) 791b15c8340SDag-Erling Smørgrav status = SSH2_FX_FAILURE; 792b15c8340SDag-Erling Smørgrav else { 793f7167e0eSDag-Erling Smørgrav if (!(handle_to_flags(handle) & O_APPEND) && 794f7167e0eSDag-Erling Smørgrav lseek(fd, off, SEEK_SET) < 0) { 795b66f2d16SKris Kennaway status = errno_to_portable(errno); 796b66f2d16SKris Kennaway error("process_write: seek failed"); 797b66f2d16SKris Kennaway } else { 798b66f2d16SKris Kennaway /* XXX ATOMICIO ? */ 799b66f2d16SKris Kennaway ret = write(fd, data, len); 800043840dfSDag-Erling Smørgrav if (ret < 0) { 801b66f2d16SKris Kennaway error("process_write: write failed"); 802b66f2d16SKris Kennaway status = errno_to_portable(errno); 803043840dfSDag-Erling Smørgrav } else if ((size_t)ret == len) { 8041e8db6e2SBrian Feldman status = SSH2_FX_OK; 805761efaa7SDag-Erling Smørgrav handle_update_write(handle, ret); 806b66f2d16SKris Kennaway } else { 807761efaa7SDag-Erling Smørgrav debug2("nothing at all written"); 808b15c8340SDag-Erling Smørgrav status = SSH2_FX_FAILURE; 809b66f2d16SKris Kennaway } 810b66f2d16SKris Kennaway } 811b66f2d16SKris Kennaway } 812b66f2d16SKris Kennaway send_status(id, status); 813e4a9863fSDag-Erling Smørgrav free(data); 814b66f2d16SKris Kennaway } 815b66f2d16SKris Kennaway 816ae1f160dSDag-Erling Smørgrav static void 817f7167e0eSDag-Erling Smørgrav process_do_stat(u_int32_t id, int do_lstat) 818b66f2d16SKris Kennaway { 8191e8db6e2SBrian Feldman Attrib a; 820b66f2d16SKris Kennaway struct stat st; 821b66f2d16SKris Kennaway char *name; 822bc5531deSDag-Erling Smørgrav int r, status = SSH2_FX_FAILURE; 823b66f2d16SKris Kennaway 824bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) 825bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 826bc5531deSDag-Erling Smørgrav 827761efaa7SDag-Erling Smørgrav debug3("request %u: %sstat", id, do_lstat ? "l" : ""); 828761efaa7SDag-Erling Smørgrav verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); 829bc5531deSDag-Erling Smørgrav r = do_lstat ? lstat(name, &st) : stat(name, &st); 830bc5531deSDag-Erling Smørgrav if (r < 0) { 831b66f2d16SKris Kennaway status = errno_to_portable(errno); 832b66f2d16SKris Kennaway } else { 8331e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 8341e8db6e2SBrian Feldman send_attrib(id, &a); 8351e8db6e2SBrian Feldman status = SSH2_FX_OK; 836b66f2d16SKris Kennaway } 8371e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 838b66f2d16SKris Kennaway send_status(id, status); 839e4a9863fSDag-Erling Smørgrav free(name); 840b66f2d16SKris Kennaway } 841b66f2d16SKris Kennaway 842ae1f160dSDag-Erling Smørgrav static void 843f7167e0eSDag-Erling Smørgrav process_stat(u_int32_t id) 844b66f2d16SKris Kennaway { 845f7167e0eSDag-Erling Smørgrav process_do_stat(id, 0); 846b66f2d16SKris Kennaway } 847b66f2d16SKris Kennaway 848ae1f160dSDag-Erling Smørgrav static void 849f7167e0eSDag-Erling Smørgrav process_lstat(u_int32_t id) 850b66f2d16SKris Kennaway { 851f7167e0eSDag-Erling Smørgrav process_do_stat(id, 1); 852b66f2d16SKris Kennaway } 853b66f2d16SKris Kennaway 854ae1f160dSDag-Erling Smørgrav static void 855f7167e0eSDag-Erling Smørgrav process_fstat(u_int32_t id) 856b66f2d16SKris Kennaway { 8571e8db6e2SBrian Feldman Attrib a; 858b66f2d16SKris Kennaway struct stat st; 859bc5531deSDag-Erling Smørgrav int fd, r, handle, status = SSH2_FX_FAILURE; 860b66f2d16SKris Kennaway 861bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 862bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 863761efaa7SDag-Erling Smørgrav debug("request %u: fstat \"%s\" (handle %u)", 864761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle); 865b66f2d16SKris Kennaway fd = handle_to_fd(handle); 866b66f2d16SKris Kennaway if (fd >= 0) { 867bc5531deSDag-Erling Smørgrav r = fstat(fd, &st); 868bc5531deSDag-Erling Smørgrav if (r < 0) { 869b66f2d16SKris Kennaway status = errno_to_portable(errno); 870b66f2d16SKris Kennaway } else { 8711e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 8721e8db6e2SBrian Feldman send_attrib(id, &a); 8731e8db6e2SBrian Feldman status = SSH2_FX_OK; 874b66f2d16SKris Kennaway } 875b66f2d16SKris Kennaway } 8761e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 877b66f2d16SKris Kennaway send_status(id, status); 878b66f2d16SKris Kennaway } 879b66f2d16SKris Kennaway 880ae1f160dSDag-Erling Smørgrav static struct timeval * 881efcad6b7SDag-Erling Smørgrav attrib_to_tv(const Attrib *a) 882b66f2d16SKris Kennaway { 883b66f2d16SKris Kennaway static struct timeval tv[2]; 8841e8db6e2SBrian Feldman 885b66f2d16SKris Kennaway tv[0].tv_sec = a->atime; 886b66f2d16SKris Kennaway tv[0].tv_usec = 0; 887b66f2d16SKris Kennaway tv[1].tv_sec = a->mtime; 888b66f2d16SKris Kennaway tv[1].tv_usec = 0; 889b66f2d16SKris Kennaway return tv; 890b66f2d16SKris Kennaway } 891b66f2d16SKris Kennaway 892ae1f160dSDag-Erling Smørgrav static void 893f7167e0eSDag-Erling Smørgrav process_setstat(u_int32_t id) 894b66f2d16SKris Kennaway { 895bc5531deSDag-Erling Smørgrav Attrib a; 896b66f2d16SKris Kennaway char *name; 897bc5531deSDag-Erling Smørgrav int r, status = SSH2_FX_OK; 898b66f2d16SKris Kennaway 899bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 900bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 901bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 902bc5531deSDag-Erling Smørgrav 903761efaa7SDag-Erling Smørgrav debug("request %u: setstat name \"%s\"", id, name); 904bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { 905d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu", 906bc5531deSDag-Erling Smørgrav name, (unsigned long long)a.size); 907bc5531deSDag-Erling Smørgrav r = truncate(name, a.size); 908bc5531deSDag-Erling Smørgrav if (r == -1) 909ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 910ae1f160dSDag-Erling Smørgrav } 911bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 912bc5531deSDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a.perm); 913bc5531deSDag-Erling Smørgrav r = chmod(name, a.perm & 07777); 914bc5531deSDag-Erling Smørgrav if (r == -1) 915b66f2d16SKris Kennaway status = errno_to_portable(errno); 916b66f2d16SKris Kennaway } 917bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 918761efaa7SDag-Erling Smørgrav char buf[64]; 919bc5531deSDag-Erling Smørgrav time_t t = a.mtime; 920761efaa7SDag-Erling Smørgrav 921761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 922761efaa7SDag-Erling Smørgrav localtime(&t)); 923761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf); 924bc5531deSDag-Erling Smørgrav r = utimes(name, attrib_to_tv(&a)); 925bc5531deSDag-Erling Smørgrav if (r == -1) 926b66f2d16SKris Kennaway status = errno_to_portable(errno); 927b66f2d16SKris Kennaway } 928bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { 929761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name, 930bc5531deSDag-Erling Smørgrav (u_long)a.uid, (u_long)a.gid); 931bc5531deSDag-Erling Smørgrav r = chown(name, a.uid, a.gid); 932bc5531deSDag-Erling Smørgrav if (r == -1) 9331e8db6e2SBrian Feldman status = errno_to_portable(errno); 9341e8db6e2SBrian Feldman } 935b66f2d16SKris Kennaway send_status(id, status); 936e4a9863fSDag-Erling Smørgrav free(name); 937b66f2d16SKris Kennaway } 938b66f2d16SKris Kennaway 939ae1f160dSDag-Erling Smørgrav static void 940f7167e0eSDag-Erling Smørgrav process_fsetstat(u_int32_t id) 941b66f2d16SKris Kennaway { 942bc5531deSDag-Erling Smørgrav Attrib a; 943bc5531deSDag-Erling Smørgrav int handle, fd, r; 9441e8db6e2SBrian Feldman int status = SSH2_FX_OK; 945b66f2d16SKris Kennaway 946bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0 || 947bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 948bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 949bc5531deSDag-Erling Smørgrav 950761efaa7SDag-Erling Smørgrav debug("request %u: fsetstat handle %d", id, handle); 951b66f2d16SKris Kennaway fd = handle_to_fd(handle); 952b15c8340SDag-Erling Smørgrav if (fd < 0) 9531e8db6e2SBrian Feldman status = SSH2_FX_FAILURE; 954b15c8340SDag-Erling Smørgrav else { 955761efaa7SDag-Erling Smørgrav char *name = handle_to_name(handle); 956761efaa7SDag-Erling Smørgrav 957bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { 958d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu", 959bc5531deSDag-Erling Smørgrav name, (unsigned long long)a.size); 960bc5531deSDag-Erling Smørgrav r = ftruncate(fd, a.size); 961bc5531deSDag-Erling Smørgrav if (r == -1) 962ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 963ae1f160dSDag-Erling Smørgrav } 964bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 965bc5531deSDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a.perm); 96683d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 967bc5531deSDag-Erling Smørgrav r = fchmod(fd, a.perm & 07777); 96883d2307dSDag-Erling Smørgrav #else 969bc5531deSDag-Erling Smørgrav r = chmod(name, a.perm & 07777); 97083d2307dSDag-Erling Smørgrav #endif 971bc5531deSDag-Erling Smørgrav if (r == -1) 972b66f2d16SKris Kennaway status = errno_to_portable(errno); 973b66f2d16SKris Kennaway } 974bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 975761efaa7SDag-Erling Smørgrav char buf[64]; 976bc5531deSDag-Erling Smørgrav time_t t = a.mtime; 977761efaa7SDag-Erling Smørgrav 978761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 979761efaa7SDag-Erling Smørgrav localtime(&t)); 980761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf); 98183d2307dSDag-Erling Smørgrav #ifdef HAVE_FUTIMES 982bc5531deSDag-Erling Smørgrav r = futimes(fd, attrib_to_tv(&a)); 98383d2307dSDag-Erling Smørgrav #else 984bc5531deSDag-Erling Smørgrav r = utimes(name, attrib_to_tv(&a)); 98583d2307dSDag-Erling Smørgrav #endif 986bc5531deSDag-Erling Smørgrav if (r == -1) 987b66f2d16SKris Kennaway status = errno_to_portable(errno); 988b66f2d16SKris Kennaway } 989bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { 990761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name, 991bc5531deSDag-Erling Smørgrav (u_long)a.uid, (u_long)a.gid); 99283d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHOWN 993bc5531deSDag-Erling Smørgrav r = fchown(fd, a.uid, a.gid); 99483d2307dSDag-Erling Smørgrav #else 995bc5531deSDag-Erling Smørgrav r = chown(name, a.uid, a.gid); 99683d2307dSDag-Erling Smørgrav #endif 997bc5531deSDag-Erling Smørgrav if (r == -1) 9981e8db6e2SBrian Feldman status = errno_to_portable(errno); 9991e8db6e2SBrian Feldman } 1000b66f2d16SKris Kennaway } 1001b66f2d16SKris Kennaway send_status(id, status); 1002b66f2d16SKris Kennaway } 1003b66f2d16SKris Kennaway 1004ae1f160dSDag-Erling Smørgrav static void 1005f7167e0eSDag-Erling Smørgrav process_opendir(u_int32_t id) 1006b66f2d16SKris Kennaway { 1007b66f2d16SKris Kennaway DIR *dirp = NULL; 1008b66f2d16SKris Kennaway char *path; 1009bc5531deSDag-Erling Smørgrav int r, handle, status = SSH2_FX_FAILURE; 1010b66f2d16SKris Kennaway 1011bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 1012bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1013bc5531deSDag-Erling Smørgrav 1014761efaa7SDag-Erling Smørgrav debug3("request %u: opendir", id); 1015761efaa7SDag-Erling Smørgrav logit("opendir \"%s\"", path); 1016b66f2d16SKris Kennaway dirp = opendir(path); 1017b66f2d16SKris Kennaway if (dirp == NULL) { 1018b66f2d16SKris Kennaway status = errno_to_portable(errno); 1019b66f2d16SKris Kennaway } else { 1020f7167e0eSDag-Erling Smørgrav handle = handle_new(HANDLE_DIR, path, 0, 0, dirp); 1021b66f2d16SKris Kennaway if (handle < 0) { 1022b66f2d16SKris Kennaway closedir(dirp); 1023b66f2d16SKris Kennaway } else { 1024b66f2d16SKris Kennaway send_handle(id, handle); 10251e8db6e2SBrian Feldman status = SSH2_FX_OK; 1026b66f2d16SKris Kennaway } 1027b66f2d16SKris Kennaway 1028b66f2d16SKris Kennaway } 10291e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 1030b66f2d16SKris Kennaway send_status(id, status); 1031e4a9863fSDag-Erling Smørgrav free(path); 1032b66f2d16SKris Kennaway } 1033b66f2d16SKris Kennaway 1034ae1f160dSDag-Erling Smørgrav static void 1035f7167e0eSDag-Erling Smørgrav process_readdir(u_int32_t id) 1036b66f2d16SKris Kennaway { 1037b66f2d16SKris Kennaway DIR *dirp; 1038b66f2d16SKris Kennaway struct dirent *dp; 1039b66f2d16SKris Kennaway char *path; 1040bc5531deSDag-Erling Smørgrav int r, handle; 1041b66f2d16SKris Kennaway 1042bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 1043bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1044bc5531deSDag-Erling Smørgrav 1045761efaa7SDag-Erling Smørgrav debug("request %u: readdir \"%s\" (handle %d)", id, 1046761efaa7SDag-Erling Smørgrav handle_to_name(handle), handle); 1047b66f2d16SKris Kennaway dirp = handle_to_dir(handle); 1048b66f2d16SKris Kennaway path = handle_to_name(handle); 1049b66f2d16SKris Kennaway if (dirp == NULL || path == NULL) { 10501e8db6e2SBrian Feldman send_status(id, SSH2_FX_FAILURE); 1051b66f2d16SKris Kennaway } else { 1052b66f2d16SKris Kennaway struct stat st; 1053bc5531deSDag-Erling Smørgrav char pathname[PATH_MAX]; 1054b66f2d16SKris Kennaway Stat *stats; 1055b66f2d16SKris Kennaway int nstats = 10, count = 0, i; 1056ee21a45fSDag-Erling Smørgrav 1057761efaa7SDag-Erling Smørgrav stats = xcalloc(nstats, sizeof(Stat)); 1058b66f2d16SKris Kennaway while ((dp = readdir(dirp)) != NULL) { 1059b66f2d16SKris Kennaway if (count >= nstats) { 1060b66f2d16SKris Kennaway nstats *= 2; 1061557f75e5SDag-Erling Smørgrav stats = xreallocarray(stats, nstats, sizeof(Stat)); 1062b66f2d16SKris Kennaway } 1063b66f2d16SKris Kennaway /* XXX OVERFLOW ? */ 1064ae1f160dSDag-Erling Smørgrav snprintf(pathname, sizeof pathname, "%s%s%s", path, 1065ae1f160dSDag-Erling Smørgrav strcmp(path, "/") ? "/" : "", dp->d_name); 1066b66f2d16SKris Kennaway if (lstat(pathname, &st) < 0) 1067b66f2d16SKris Kennaway continue; 10681e8db6e2SBrian Feldman stat_to_attrib(&st, &(stats[count].attrib)); 1069b66f2d16SKris Kennaway stats[count].name = xstrdup(dp->d_name); 1070b15c8340SDag-Erling Smørgrav stats[count].long_name = ls_file(dp->d_name, &st, 0, 0); 1071b66f2d16SKris Kennaway count++; 1072b66f2d16SKris Kennaway /* send up to 100 entries in one message */ 10731e8db6e2SBrian Feldman /* XXX check packet size instead */ 1074b66f2d16SKris Kennaway if (count == 100) 1075b66f2d16SKris Kennaway break; 1076b66f2d16SKris Kennaway } 10771e8db6e2SBrian Feldman if (count > 0) { 1078b66f2d16SKris Kennaway send_names(id, count, stats); 1079b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 1080e4a9863fSDag-Erling Smørgrav free(stats[i].name); 1081e4a9863fSDag-Erling Smørgrav free(stats[i].long_name); 1082b66f2d16SKris Kennaway } 10831e8db6e2SBrian Feldman } else { 10841e8db6e2SBrian Feldman send_status(id, SSH2_FX_EOF); 10851e8db6e2SBrian Feldman } 1086e4a9863fSDag-Erling Smørgrav free(stats); 1087b66f2d16SKris Kennaway } 1088b66f2d16SKris Kennaway } 1089b66f2d16SKris Kennaway 1090ae1f160dSDag-Erling Smørgrav static void 1091f7167e0eSDag-Erling Smørgrav process_remove(u_int32_t id) 1092b66f2d16SKris Kennaway { 1093b66f2d16SKris Kennaway char *name; 1094bc5531deSDag-Erling Smørgrav int r, status = SSH2_FX_FAILURE; 1095b66f2d16SKris Kennaway 1096bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) 1097bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1098bc5531deSDag-Erling Smørgrav 1099761efaa7SDag-Erling Smørgrav debug3("request %u: remove", id); 1100761efaa7SDag-Erling Smørgrav logit("remove name \"%s\"", name); 1101bc5531deSDag-Erling Smørgrav r = unlink(name); 1102bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1103b66f2d16SKris Kennaway send_status(id, status); 1104e4a9863fSDag-Erling Smørgrav free(name); 1105b66f2d16SKris Kennaway } 1106b66f2d16SKris Kennaway 1107ae1f160dSDag-Erling Smørgrav static void 1108f7167e0eSDag-Erling Smørgrav process_mkdir(u_int32_t id) 1109b66f2d16SKris Kennaway { 1110bc5531deSDag-Erling Smørgrav Attrib a; 1111b66f2d16SKris Kennaway char *name; 1112bc5531deSDag-Erling Smørgrav int r, mode, status = SSH2_FX_FAILURE; 1113b66f2d16SKris Kennaway 1114bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 1115bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 1116bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1117bc5531deSDag-Erling Smørgrav 1118bc5531deSDag-Erling Smørgrav mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 1119bc5531deSDag-Erling Smørgrav a.perm & 07777 : 0777; 1120761efaa7SDag-Erling Smørgrav debug3("request %u: mkdir", id); 1121761efaa7SDag-Erling Smørgrav logit("mkdir name \"%s\" mode 0%o", name, mode); 1122bc5531deSDag-Erling Smørgrav r = mkdir(name, mode); 1123bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1124b66f2d16SKris Kennaway send_status(id, status); 1125e4a9863fSDag-Erling Smørgrav free(name); 1126b66f2d16SKris Kennaway } 1127b66f2d16SKris Kennaway 1128ae1f160dSDag-Erling Smørgrav static void 1129f7167e0eSDag-Erling Smørgrav process_rmdir(u_int32_t id) 1130b66f2d16SKris Kennaway { 1131b66f2d16SKris Kennaway char *name; 1132bc5531deSDag-Erling Smørgrav int r, status; 1133b66f2d16SKris Kennaway 1134bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) 1135bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1136bc5531deSDag-Erling Smørgrav 1137761efaa7SDag-Erling Smørgrav debug3("request %u: rmdir", id); 1138761efaa7SDag-Erling Smørgrav logit("rmdir name \"%s\"", name); 1139bc5531deSDag-Erling Smørgrav r = rmdir(name); 1140bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1141b66f2d16SKris Kennaway send_status(id, status); 1142e4a9863fSDag-Erling Smørgrav free(name); 1143b66f2d16SKris Kennaway } 1144b66f2d16SKris Kennaway 1145ae1f160dSDag-Erling Smørgrav static void 1146f7167e0eSDag-Erling Smørgrav process_realpath(u_int32_t id) 1147b66f2d16SKris Kennaway { 1148bc5531deSDag-Erling Smørgrav char resolvedname[PATH_MAX]; 1149b66f2d16SKris Kennaway char *path; 1150bc5531deSDag-Erling Smørgrav int r; 1151b66f2d16SKris Kennaway 1152bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 1153bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1154bc5531deSDag-Erling Smørgrav 11551e8db6e2SBrian Feldman if (path[0] == '\0') { 1156e4a9863fSDag-Erling Smørgrav free(path); 11571e8db6e2SBrian Feldman path = xstrdup("."); 11581e8db6e2SBrian Feldman } 1159761efaa7SDag-Erling Smørgrav debug3("request %u: realpath", id); 1160761efaa7SDag-Erling Smørgrav verbose("realpath \"%s\"", path); 1161b66f2d16SKris Kennaway if (realpath(path, resolvedname) == NULL) { 1162b66f2d16SKris Kennaway send_status(id, errno_to_portable(errno)); 1163b66f2d16SKris Kennaway } else { 1164b66f2d16SKris Kennaway Stat s; 1165b66f2d16SKris Kennaway attrib_clear(&s.attrib); 1166b66f2d16SKris Kennaway s.name = s.long_name = resolvedname; 1167b66f2d16SKris Kennaway send_names(id, 1, &s); 1168b66f2d16SKris Kennaway } 1169e4a9863fSDag-Erling Smørgrav free(path); 1170b66f2d16SKris Kennaway } 1171b66f2d16SKris Kennaway 1172ae1f160dSDag-Erling Smørgrav static void 1173f7167e0eSDag-Erling Smørgrav process_rename(u_int32_t id) 1174b66f2d16SKris Kennaway { 1175b66f2d16SKris Kennaway char *oldpath, *newpath; 1176bc5531deSDag-Erling Smørgrav int r, status; 1177d0c8c0bcSDag-Erling Smørgrav struct stat sb; 1178b66f2d16SKris Kennaway 1179bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1180bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 1181bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1182bc5531deSDag-Erling Smørgrav 1183761efaa7SDag-Erling Smørgrav debug3("request %u: rename", id); 1184761efaa7SDag-Erling Smørgrav logit("rename old \"%s\" new \"%s\"", oldpath, newpath); 1185d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_FAILURE; 1186f7167e0eSDag-Erling Smørgrav if (lstat(oldpath, &sb) == -1) 1187d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1188d0c8c0bcSDag-Erling Smørgrav else if (S_ISREG(sb.st_mode)) { 1189d0c8c0bcSDag-Erling Smørgrav /* Race-free rename of regular files */ 1190d74d50a8SDag-Erling Smørgrav if (link(oldpath, newpath) == -1) { 11917aee6ffeSDag-Erling Smørgrav if (errno == EOPNOTSUPP || errno == ENOSYS 1192d4af9e69SDag-Erling Smørgrav #ifdef EXDEV 1193d4af9e69SDag-Erling Smørgrav || errno == EXDEV 1194d4af9e69SDag-Erling Smørgrav #endif 1195d74d50a8SDag-Erling Smørgrav #ifdef LINK_OPNOTSUPP_ERRNO 1196d74d50a8SDag-Erling Smørgrav || errno == LINK_OPNOTSUPP_ERRNO 1197d74d50a8SDag-Erling Smørgrav #endif 1198d74d50a8SDag-Erling Smørgrav ) { 1199d74d50a8SDag-Erling Smørgrav struct stat st; 1200d74d50a8SDag-Erling Smørgrav 1201d74d50a8SDag-Erling Smørgrav /* 1202d74d50a8SDag-Erling Smørgrav * fs doesn't support links, so fall back to 1203d74d50a8SDag-Erling Smørgrav * stat+rename. This is racy. 1204d74d50a8SDag-Erling Smørgrav */ 1205d74d50a8SDag-Erling Smørgrav if (stat(newpath, &st) == -1) { 1206d74d50a8SDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1207d74d50a8SDag-Erling Smørgrav status = 1208d74d50a8SDag-Erling Smørgrav errno_to_portable(errno); 1209d74d50a8SDag-Erling Smørgrav else 1210d74d50a8SDag-Erling Smørgrav status = SSH2_FX_OK; 1211d74d50a8SDag-Erling Smørgrav } 1212d74d50a8SDag-Erling Smørgrav } else { 1213d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1214d74d50a8SDag-Erling Smørgrav } 1215d74d50a8SDag-Erling Smørgrav } else if (unlink(oldpath) == -1) { 1216d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1217d0c8c0bcSDag-Erling Smørgrav /* clean spare link */ 1218d0c8c0bcSDag-Erling Smørgrav unlink(newpath); 1219d0c8c0bcSDag-Erling Smørgrav } else 1220d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 1221d0c8c0bcSDag-Erling Smørgrav } else if (stat(newpath, &sb) == -1) { 1222d0c8c0bcSDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1223d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1224d0c8c0bcSDag-Erling Smørgrav else 1225d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 12261e8db6e2SBrian Feldman } 1227b66f2d16SKris Kennaway send_status(id, status); 1228e4a9863fSDag-Erling Smørgrav free(oldpath); 1229e4a9863fSDag-Erling Smørgrav free(newpath); 1230b66f2d16SKris Kennaway } 1231b66f2d16SKris Kennaway 1232ae1f160dSDag-Erling Smørgrav static void 1233f7167e0eSDag-Erling Smørgrav process_readlink(u_int32_t id) 12341e8db6e2SBrian Feldman { 1235bc5531deSDag-Erling Smørgrav int r, len; 1236bc5531deSDag-Erling Smørgrav char buf[PATH_MAX]; 12371e8db6e2SBrian Feldman char *path; 12381e8db6e2SBrian Feldman 1239bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 1240bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1241bc5531deSDag-Erling Smørgrav 1242761efaa7SDag-Erling Smørgrav debug3("request %u: readlink", id); 1243761efaa7SDag-Erling Smørgrav verbose("readlink \"%s\"", path); 1244d74d50a8SDag-Erling Smørgrav if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) 12451e8db6e2SBrian Feldman send_status(id, errno_to_portable(errno)); 12461e8db6e2SBrian Feldman else { 12471e8db6e2SBrian Feldman Stat s; 12481e8db6e2SBrian Feldman 1249d74d50a8SDag-Erling Smørgrav buf[len] = '\0'; 12501e8db6e2SBrian Feldman attrib_clear(&s.attrib); 1251d74d50a8SDag-Erling Smørgrav s.name = s.long_name = buf; 12521e8db6e2SBrian Feldman send_names(id, 1, &s); 12531e8db6e2SBrian Feldman } 1254e4a9863fSDag-Erling Smørgrav free(path); 12551e8db6e2SBrian Feldman } 12561e8db6e2SBrian Feldman 1257ae1f160dSDag-Erling Smørgrav static void 1258f7167e0eSDag-Erling Smørgrav process_symlink(u_int32_t id) 12591e8db6e2SBrian Feldman { 12601e8db6e2SBrian Feldman char *oldpath, *newpath; 1261bc5531deSDag-Erling Smørgrav int r, status; 12621e8db6e2SBrian Feldman 1263bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1264bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 1265bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1266bc5531deSDag-Erling Smørgrav 1267761efaa7SDag-Erling Smørgrav debug3("request %u: symlink", id); 1268761efaa7SDag-Erling Smørgrav logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); 1269d0c8c0bcSDag-Erling Smørgrav /* this will fail if 'newpath' exists */ 1270bc5531deSDag-Erling Smørgrav r = symlink(oldpath, newpath); 1271bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 12721e8db6e2SBrian Feldman send_status(id, status); 1273e4a9863fSDag-Erling Smørgrav free(oldpath); 1274e4a9863fSDag-Erling Smørgrav free(newpath); 12751e8db6e2SBrian Feldman } 12761e8db6e2SBrian Feldman 1277ae1f160dSDag-Erling Smørgrav static void 1278d4af9e69SDag-Erling Smørgrav process_extended_posix_rename(u_int32_t id) 1279d4af9e69SDag-Erling Smørgrav { 1280d4af9e69SDag-Erling Smørgrav char *oldpath, *newpath; 1281bc5531deSDag-Erling Smørgrav int r, status; 1282d4af9e69SDag-Erling Smørgrav 1283bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1284bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 1285bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1286bc5531deSDag-Erling Smørgrav 1287d4af9e69SDag-Erling Smørgrav debug3("request %u: posix-rename", id); 1288d4af9e69SDag-Erling Smørgrav logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); 1289bc5531deSDag-Erling Smørgrav r = rename(oldpath, newpath); 1290bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1291b15c8340SDag-Erling Smørgrav send_status(id, status); 1292e4a9863fSDag-Erling Smørgrav free(oldpath); 1293e4a9863fSDag-Erling Smørgrav free(newpath); 1294d4af9e69SDag-Erling Smørgrav } 1295d4af9e69SDag-Erling Smørgrav 1296d4af9e69SDag-Erling Smørgrav static void 1297d4af9e69SDag-Erling Smørgrav process_extended_statvfs(u_int32_t id) 1298d4af9e69SDag-Erling Smørgrav { 1299d4af9e69SDag-Erling Smørgrav char *path; 1300d4af9e69SDag-Erling Smørgrav struct statvfs st; 1301bc5531deSDag-Erling Smørgrav int r; 1302d4af9e69SDag-Erling Smørgrav 1303bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 1304bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1305f7167e0eSDag-Erling Smørgrav debug3("request %u: statvfs", id); 1306f7167e0eSDag-Erling Smørgrav logit("statvfs \"%s\"", path); 1307d4af9e69SDag-Erling Smørgrav 1308d4af9e69SDag-Erling Smørgrav if (statvfs(path, &st) != 0) 1309d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1310d4af9e69SDag-Erling Smørgrav else 1311d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st); 1312e4a9863fSDag-Erling Smørgrav free(path); 1313d4af9e69SDag-Erling Smørgrav } 1314d4af9e69SDag-Erling Smørgrav 1315d4af9e69SDag-Erling Smørgrav static void 1316d4af9e69SDag-Erling Smørgrav process_extended_fstatvfs(u_int32_t id) 1317d4af9e69SDag-Erling Smørgrav { 1318bc5531deSDag-Erling Smørgrav int r, handle, fd; 1319d4af9e69SDag-Erling Smørgrav struct statvfs st; 1320d4af9e69SDag-Erling Smørgrav 1321bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 1322bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1323d4af9e69SDag-Erling Smørgrav debug("request %u: fstatvfs \"%s\" (handle %u)", 1324d4af9e69SDag-Erling Smørgrav id, handle_to_name(handle), handle); 1325d4af9e69SDag-Erling Smørgrav if ((fd = handle_to_fd(handle)) < 0) { 1326d4af9e69SDag-Erling Smørgrav send_status(id, SSH2_FX_FAILURE); 1327d4af9e69SDag-Erling Smørgrav return; 1328d4af9e69SDag-Erling Smørgrav } 1329d4af9e69SDag-Erling Smørgrav if (fstatvfs(fd, &st) != 0) 1330d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1331d4af9e69SDag-Erling Smørgrav else 1332d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st); 1333d4af9e69SDag-Erling Smørgrav } 1334d4af9e69SDag-Erling Smørgrav 1335d4af9e69SDag-Erling Smørgrav static void 13364a421b63SDag-Erling Smørgrav process_extended_hardlink(u_int32_t id) 13374a421b63SDag-Erling Smørgrav { 13384a421b63SDag-Erling Smørgrav char *oldpath, *newpath; 1339bc5531deSDag-Erling Smørgrav int r, status; 13404a421b63SDag-Erling Smørgrav 1341bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1342bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 1343bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1344bc5531deSDag-Erling Smørgrav 13454a421b63SDag-Erling Smørgrav debug3("request %u: hardlink", id); 13464a421b63SDag-Erling Smørgrav logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath); 1347bc5531deSDag-Erling Smørgrav r = link(oldpath, newpath); 1348bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 13494a421b63SDag-Erling Smørgrav send_status(id, status); 1350e4a9863fSDag-Erling Smørgrav free(oldpath); 1351e4a9863fSDag-Erling Smørgrav free(newpath); 13524a421b63SDag-Erling Smørgrav } 13534a421b63SDag-Erling Smørgrav 13544a421b63SDag-Erling Smørgrav static void 1355f7167e0eSDag-Erling Smørgrav process_extended_fsync(u_int32_t id) 13561e8db6e2SBrian Feldman { 1357bc5531deSDag-Erling Smørgrav int handle, fd, r, status = SSH2_FX_OP_UNSUPPORTED; 13581e8db6e2SBrian Feldman 1359bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 1360bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1361f7167e0eSDag-Erling Smørgrav debug3("request %u: fsync (handle %u)", id, handle); 1362f7167e0eSDag-Erling Smørgrav verbose("fsync \"%s\"", handle_to_name(handle)); 1363f7167e0eSDag-Erling Smørgrav if ((fd = handle_to_fd(handle)) < 0) 1364f7167e0eSDag-Erling Smørgrav status = SSH2_FX_NO_SUCH_FILE; 1365f7167e0eSDag-Erling Smørgrav else if (handle_is_ok(handle, HANDLE_FILE)) { 1366bc5531deSDag-Erling Smørgrav r = fsync(fd); 1367bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1368f7167e0eSDag-Erling Smørgrav } 1369f7167e0eSDag-Erling Smørgrav send_status(id, status); 1370f7167e0eSDag-Erling Smørgrav } 1371f7167e0eSDag-Erling Smørgrav 1372f7167e0eSDag-Erling Smørgrav static void 1373f7167e0eSDag-Erling Smørgrav process_extended(u_int32_t id) 1374f7167e0eSDag-Erling Smørgrav { 1375f7167e0eSDag-Erling Smørgrav char *request; 1376bc5531deSDag-Erling Smørgrav int i, r; 1377f7167e0eSDag-Erling Smørgrav 1378bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &request, NULL)) != 0) 1379bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1380f7167e0eSDag-Erling Smørgrav for (i = 0; extended_handlers[i].handler != NULL; i++) { 1381f7167e0eSDag-Erling Smørgrav if (strcmp(request, extended_handlers[i].ext_name) == 0) { 1382f7167e0eSDag-Erling Smørgrav if (!request_permitted(&extended_handlers[i])) 1383f7167e0eSDag-Erling Smørgrav send_status(id, SSH2_FX_PERMISSION_DENIED); 1384d4af9e69SDag-Erling Smørgrav else 1385f7167e0eSDag-Erling Smørgrav extended_handlers[i].handler(id); 1386f7167e0eSDag-Erling Smørgrav break; 1387f7167e0eSDag-Erling Smørgrav } 1388f7167e0eSDag-Erling Smørgrav } 1389f7167e0eSDag-Erling Smørgrav if (extended_handlers[i].handler == NULL) { 1390f7167e0eSDag-Erling Smørgrav error("Unknown extended request \"%.100s\"", request); 13911e8db6e2SBrian Feldman send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 1392f7167e0eSDag-Erling Smørgrav } 1393e4a9863fSDag-Erling Smørgrav free(request); 13941e8db6e2SBrian Feldman } 1395b66f2d16SKris Kennaway 1396b66f2d16SKris Kennaway /* stolen from ssh-agent */ 1397b66f2d16SKris Kennaway 1398ae1f160dSDag-Erling Smørgrav static void 1399b66f2d16SKris Kennaway process(void) 1400b66f2d16SKris Kennaway { 1401bc5531deSDag-Erling Smørgrav u_int msg_len; 1402bc5531deSDag-Erling Smørgrav u_int buf_len; 1403bc5531deSDag-Erling Smørgrav u_int consumed; 1404bc5531deSDag-Erling Smørgrav u_char type; 1405bc5531deSDag-Erling Smørgrav const u_char *cp; 1406bc5531deSDag-Erling Smørgrav int i, r; 1407f7167e0eSDag-Erling Smørgrav u_int32_t id; 1408b66f2d16SKris Kennaway 1409bc5531deSDag-Erling Smørgrav buf_len = sshbuf_len(iqueue); 1410545d5ecaSDag-Erling Smørgrav if (buf_len < 5) 1411b66f2d16SKris Kennaway return; /* Incomplete message. */ 1412bc5531deSDag-Erling Smørgrav cp = sshbuf_ptr(iqueue); 1413761efaa7SDag-Erling Smørgrav msg_len = get_u32(cp); 1414021d409fSDag-Erling Smørgrav if (msg_len > SFTP_MAX_MSG_LENGTH) { 1415761efaa7SDag-Erling Smørgrav error("bad message from %s local user %s", 1416761efaa7SDag-Erling Smørgrav client_addr, pw->pw_name); 1417d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(11); 1418b66f2d16SKris Kennaway } 1419545d5ecaSDag-Erling Smørgrav if (buf_len < msg_len + 4) 1420b66f2d16SKris Kennaway return; 1421bc5531deSDag-Erling Smørgrav if ((r = sshbuf_consume(iqueue, 4)) != 0) 1422bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1423545d5ecaSDag-Erling Smørgrav buf_len -= 4; 1424bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u8(iqueue, &type)) != 0) 1425bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1426f7167e0eSDag-Erling Smørgrav 1427b66f2d16SKris Kennaway switch (type) { 14281e8db6e2SBrian Feldman case SSH2_FXP_INIT: 1429b66f2d16SKris Kennaway process_init(); 1430f7167e0eSDag-Erling Smørgrav init_done = 1; 14311e8db6e2SBrian Feldman break; 14321e8db6e2SBrian Feldman case SSH2_FXP_EXTENDED: 1433f7167e0eSDag-Erling Smørgrav if (!init_done) 1434f7167e0eSDag-Erling Smørgrav fatal("Received extended request before init"); 1435bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(iqueue, &id)) != 0) 1436bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1437f7167e0eSDag-Erling Smørgrav process_extended(id); 14381e8db6e2SBrian Feldman break; 1439b66f2d16SKris Kennaway default: 1440f7167e0eSDag-Erling Smørgrav if (!init_done) 1441f7167e0eSDag-Erling Smørgrav fatal("Received %u request before init", type); 1442bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(iqueue, &id)) != 0) 1443bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1444f7167e0eSDag-Erling Smørgrav for (i = 0; handlers[i].handler != NULL; i++) { 1445f7167e0eSDag-Erling Smørgrav if (type == handlers[i].type) { 1446f7167e0eSDag-Erling Smørgrav if (!request_permitted(&handlers[i])) { 1447f7167e0eSDag-Erling Smørgrav send_status(id, 1448f7167e0eSDag-Erling Smørgrav SSH2_FX_PERMISSION_DENIED); 1449f7167e0eSDag-Erling Smørgrav } else { 1450f7167e0eSDag-Erling Smørgrav handlers[i].handler(id); 1451f7167e0eSDag-Erling Smørgrav } 1452b66f2d16SKris Kennaway break; 1453b66f2d16SKris Kennaway } 1454f7167e0eSDag-Erling Smørgrav } 1455f7167e0eSDag-Erling Smørgrav if (handlers[i].handler == NULL) 1456f7167e0eSDag-Erling Smørgrav error("Unknown message %u", type); 1457f7167e0eSDag-Erling Smørgrav } 1458545d5ecaSDag-Erling Smørgrav /* discard the remaining bytes from the current packet */ 1459bc5531deSDag-Erling Smørgrav if (buf_len < sshbuf_len(iqueue)) { 1460d4af9e69SDag-Erling Smørgrav error("iqueue grew unexpectedly"); 1461d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1462d4af9e69SDag-Erling Smørgrav } 1463bc5531deSDag-Erling Smørgrav consumed = buf_len - sshbuf_len(iqueue); 1464d4af9e69SDag-Erling Smørgrav if (msg_len < consumed) { 1465f7167e0eSDag-Erling Smørgrav error("msg_len %u < consumed %u", msg_len, consumed); 1466d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1467d4af9e69SDag-Erling Smørgrav } 1468bc5531deSDag-Erling Smørgrav if (msg_len > consumed && 1469bc5531deSDag-Erling Smørgrav (r = sshbuf_consume(iqueue, msg_len - consumed)) != 0) 1470bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1471b66f2d16SKris Kennaway } 1472b66f2d16SKris Kennaway 1473761efaa7SDag-Erling Smørgrav /* Cleanup handler that logs active handles upon normal exit */ 1474761efaa7SDag-Erling Smørgrav void 1475d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(int i) 1476761efaa7SDag-Erling Smørgrav { 1477761efaa7SDag-Erling Smørgrav if (pw != NULL && client_addr != NULL) { 1478761efaa7SDag-Erling Smørgrav handle_log_exit(); 1479761efaa7SDag-Erling Smørgrav logit("session closed for local user %s from [%s]", 1480761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr); 1481761efaa7SDag-Erling Smørgrav } 1482761efaa7SDag-Erling Smørgrav _exit(i); 1483761efaa7SDag-Erling Smørgrav } 1484761efaa7SDag-Erling Smørgrav 1485761efaa7SDag-Erling Smørgrav static void 1486d4af9e69SDag-Erling Smørgrav sftp_server_usage(void) 1487761efaa7SDag-Erling Smørgrav { 1488761efaa7SDag-Erling Smørgrav extern char *__progname; 1489761efaa7SDag-Erling Smørgrav 1490761efaa7SDag-Erling Smørgrav fprintf(stderr, 14916888a9beSDag-Erling Smørgrav "usage: %s [-ehR] [-d start_directory] [-f log_facility] " 1492f7167e0eSDag-Erling Smørgrav "[-l log_level]\n\t[-P blacklisted_requests] " 1493f7167e0eSDag-Erling Smørgrav "[-p whitelisted_requests] [-u umask]\n" 1494f7167e0eSDag-Erling Smørgrav " %s -Q protocol_feature\n", 1495f7167e0eSDag-Erling Smørgrav __progname, __progname); 1496761efaa7SDag-Erling Smørgrav exit(1); 1497761efaa7SDag-Erling Smørgrav } 1498761efaa7SDag-Erling Smørgrav 1499b66f2d16SKris Kennaway int 1500d4af9e69SDag-Erling Smørgrav sftp_server_main(int argc, char **argv, struct passwd *user_pw) 1501b66f2d16SKris Kennaway { 15021e8db6e2SBrian Feldman fd_set *rset, *wset; 1503bc5531deSDag-Erling Smørgrav int i, r, in, out, max, ch, skipargs = 0, log_stderr = 0; 15041e8db6e2SBrian Feldman ssize_t len, olen, set_size; 1505761efaa7SDag-Erling Smørgrav SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; 15066888a9beSDag-Erling Smørgrav char *cp, *homedir = NULL, buf[4*4096]; 15074a421b63SDag-Erling Smørgrav long mask; 1508761efaa7SDag-Erling Smørgrav 1509761efaa7SDag-Erling Smørgrav extern char *optarg; 1510761efaa7SDag-Erling Smørgrav extern char *__progname; 15111e8db6e2SBrian Feldman 1512acc1a9efSDag-Erling Smørgrav ssh_malloc_init(); /* must be called before any mallocs */ 1513761efaa7SDag-Erling Smørgrav __progname = ssh_get_progname(argv[0]); 1514761efaa7SDag-Erling Smørgrav log_init(__progname, log_level, log_facility, log_stderr); 1515b66f2d16SKris Kennaway 15166888a9beSDag-Erling Smørgrav pw = pwcopy(user_pw); 15176888a9beSDag-Erling Smørgrav 1518f7167e0eSDag-Erling Smørgrav while (!skipargs && (ch = getopt(argc, argv, 1519f7167e0eSDag-Erling Smørgrav "d:f:l:P:p:Q:u:cehR")) != -1) { 1520761efaa7SDag-Erling Smørgrav switch (ch) { 1521f7167e0eSDag-Erling Smørgrav case 'Q': 1522f7167e0eSDag-Erling Smørgrav if (strcasecmp(optarg, "requests") != 0) { 1523f7167e0eSDag-Erling Smørgrav fprintf(stderr, "Invalid query type\n"); 1524f7167e0eSDag-Erling Smørgrav exit(1); 1525f7167e0eSDag-Erling Smørgrav } 1526f7167e0eSDag-Erling Smørgrav for (i = 0; handlers[i].handler != NULL; i++) 1527f7167e0eSDag-Erling Smørgrav printf("%s\n", handlers[i].name); 1528f7167e0eSDag-Erling Smørgrav for (i = 0; extended_handlers[i].handler != NULL; i++) 1529f7167e0eSDag-Erling Smørgrav printf("%s\n", extended_handlers[i].name); 1530f7167e0eSDag-Erling Smørgrav exit(0); 1531f7167e0eSDag-Erling Smørgrav break; 1532b15c8340SDag-Erling Smørgrav case 'R': 1533b15c8340SDag-Erling Smørgrav readonly = 1; 1534b15c8340SDag-Erling Smørgrav break; 1535761efaa7SDag-Erling Smørgrav case 'c': 1536761efaa7SDag-Erling Smørgrav /* 1537761efaa7SDag-Erling Smørgrav * Ignore all arguments if we are invoked as a 1538761efaa7SDag-Erling Smørgrav * shell using "sftp-server -c command" 1539761efaa7SDag-Erling Smørgrav */ 1540761efaa7SDag-Erling Smørgrav skipargs = 1; 1541761efaa7SDag-Erling Smørgrav break; 1542761efaa7SDag-Erling Smørgrav case 'e': 1543761efaa7SDag-Erling Smørgrav log_stderr = 1; 1544761efaa7SDag-Erling Smørgrav break; 1545761efaa7SDag-Erling Smørgrav case 'l': 1546761efaa7SDag-Erling Smørgrav log_level = log_level_number(optarg); 1547761efaa7SDag-Erling Smørgrav if (log_level == SYSLOG_LEVEL_NOT_SET) 1548761efaa7SDag-Erling Smørgrav error("Invalid log level \"%s\"", optarg); 1549761efaa7SDag-Erling Smørgrav break; 1550761efaa7SDag-Erling Smørgrav case 'f': 1551761efaa7SDag-Erling Smørgrav log_facility = log_facility_number(optarg); 1552d4af9e69SDag-Erling Smørgrav if (log_facility == SYSLOG_FACILITY_NOT_SET) 1553761efaa7SDag-Erling Smørgrav error("Invalid log facility \"%s\"", optarg); 1554761efaa7SDag-Erling Smørgrav break; 15556888a9beSDag-Erling Smørgrav case 'd': 15566888a9beSDag-Erling Smørgrav cp = tilde_expand_filename(optarg, user_pw->pw_uid); 15576888a9beSDag-Erling Smørgrav homedir = percent_expand(cp, "d", user_pw->pw_dir, 15586888a9beSDag-Erling Smørgrav "u", user_pw->pw_name, (char *)NULL); 15596888a9beSDag-Erling Smørgrav free(cp); 15606888a9beSDag-Erling Smørgrav break; 1561f7167e0eSDag-Erling Smørgrav case 'p': 1562f7167e0eSDag-Erling Smørgrav if (request_whitelist != NULL) 1563f7167e0eSDag-Erling Smørgrav fatal("Permitted requests already set"); 1564f7167e0eSDag-Erling Smørgrav request_whitelist = xstrdup(optarg); 1565f7167e0eSDag-Erling Smørgrav break; 1566f7167e0eSDag-Erling Smørgrav case 'P': 1567f7167e0eSDag-Erling Smørgrav if (request_blacklist != NULL) 1568f7167e0eSDag-Erling Smørgrav fatal("Refused requests already set"); 1569f7167e0eSDag-Erling Smørgrav request_blacklist = xstrdup(optarg); 1570f7167e0eSDag-Erling Smørgrav break; 1571b15c8340SDag-Erling Smørgrav case 'u': 15724a421b63SDag-Erling Smørgrav errno = 0; 15734a421b63SDag-Erling Smørgrav mask = strtol(optarg, &cp, 8); 15744a421b63SDag-Erling Smørgrav if (mask < 0 || mask > 0777 || *cp != '\0' || 15754a421b63SDag-Erling Smørgrav cp == optarg || (mask == 0 && errno != 0)) 15764a421b63SDag-Erling Smørgrav fatal("Invalid umask \"%s\"", optarg); 15774a421b63SDag-Erling Smørgrav (void)umask((mode_t)mask); 1578b15c8340SDag-Erling Smørgrav break; 1579761efaa7SDag-Erling Smørgrav case 'h': 1580761efaa7SDag-Erling Smørgrav default: 1581d4af9e69SDag-Erling Smørgrav sftp_server_usage(); 1582761efaa7SDag-Erling Smørgrav } 1583761efaa7SDag-Erling Smørgrav } 1584761efaa7SDag-Erling Smørgrav 1585761efaa7SDag-Erling Smørgrav log_init(__progname, log_level, log_facility, log_stderr); 1586761efaa7SDag-Erling Smørgrav 1587a0ee8cc6SDag-Erling Smørgrav /* 1588076ad2f8SDag-Erling Smørgrav * On platforms where we can, avoid making /proc/self/{mem,maps} 1589a0ee8cc6SDag-Erling Smørgrav * available to the user so that sftp access doesn't automatically 1590a0ee8cc6SDag-Erling Smørgrav * imply arbitrary code execution access that will break 1591a0ee8cc6SDag-Erling Smørgrav * restricted configurations. 1592a0ee8cc6SDag-Erling Smørgrav */ 1593076ad2f8SDag-Erling Smørgrav platform_disable_tracing(1); /* strict */ 1594a0ee8cc6SDag-Erling Smørgrav 1595acc1a9efSDag-Erling Smørgrav /* Drop any fine-grained privileges we don't need */ 1596acc1a9efSDag-Erling Smørgrav platform_pledge_sftp_server(); 1597acc1a9efSDag-Erling Smørgrav 1598761efaa7SDag-Erling Smørgrav if ((cp = getenv("SSH_CONNECTION")) != NULL) { 1599761efaa7SDag-Erling Smørgrav client_addr = xstrdup(cp); 1600d4af9e69SDag-Erling Smørgrav if ((cp = strchr(client_addr, ' ')) == NULL) { 1601d4af9e69SDag-Erling Smørgrav error("Malformed SSH_CONNECTION variable: \"%s\"", 1602761efaa7SDag-Erling Smørgrav getenv("SSH_CONNECTION")); 1603d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1604d4af9e69SDag-Erling Smørgrav } 1605761efaa7SDag-Erling Smørgrav *cp = '\0'; 1606761efaa7SDag-Erling Smørgrav } else 1607761efaa7SDag-Erling Smørgrav client_addr = xstrdup("UNKNOWN"); 1608761efaa7SDag-Erling Smørgrav 1609761efaa7SDag-Erling Smørgrav logit("session opened for local user %s from [%s]", 1610761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr); 1611761efaa7SDag-Erling Smørgrav 1612b15c8340SDag-Erling Smørgrav in = STDIN_FILENO; 1613b15c8340SDag-Erling Smørgrav out = STDOUT_FILENO; 1614b66f2d16SKris Kennaway 161583d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN 161683d2307dSDag-Erling Smørgrav setmode(in, O_BINARY); 161783d2307dSDag-Erling Smørgrav setmode(out, O_BINARY); 161883d2307dSDag-Erling Smørgrav #endif 161983d2307dSDag-Erling Smørgrav 1620b66f2d16SKris Kennaway max = 0; 1621b66f2d16SKris Kennaway if (in > max) 1622b66f2d16SKris Kennaway max = in; 1623b66f2d16SKris Kennaway if (out > max) 1624b66f2d16SKris Kennaway max = out; 1625b66f2d16SKris Kennaway 1626bc5531deSDag-Erling Smørgrav if ((iqueue = sshbuf_new()) == NULL) 1627bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 1628bc5531deSDag-Erling Smørgrav if ((oqueue = sshbuf_new()) == NULL) 1629bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 1630b66f2d16SKris Kennaway 1631acc1a9efSDag-Erling Smørgrav rset = xcalloc(howmany(max + 1, NFDBITS), sizeof(fd_mask)); 1632acc1a9efSDag-Erling Smørgrav wset = xcalloc(howmany(max + 1, NFDBITS), sizeof(fd_mask)); 1633b66f2d16SKris Kennaway 16346888a9beSDag-Erling Smørgrav if (homedir != NULL) { 16356888a9beSDag-Erling Smørgrav if (chdir(homedir) != 0) { 16366888a9beSDag-Erling Smørgrav error("chdir to \"%s\" failed: %s", homedir, 16376888a9beSDag-Erling Smørgrav strerror(errno)); 16386888a9beSDag-Erling Smørgrav } 16396888a9beSDag-Erling Smørgrav } 16406888a9beSDag-Erling Smørgrav 1641acc1a9efSDag-Erling Smørgrav set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 16421e8db6e2SBrian Feldman for (;;) { 16431e8db6e2SBrian Feldman memset(rset, 0, set_size); 16441e8db6e2SBrian Feldman memset(wset, 0, set_size); 16451e8db6e2SBrian Feldman 1646d4af9e69SDag-Erling Smørgrav /* 1647d4af9e69SDag-Erling Smørgrav * Ensure that we can read a full buffer and handle 1648d4af9e69SDag-Erling Smørgrav * the worst-case length packet it can generate, 1649d4af9e69SDag-Erling Smørgrav * otherwise apply backpressure by stopping reads. 1650d4af9e69SDag-Erling Smørgrav */ 1651bc5531deSDag-Erling Smørgrav if ((r = sshbuf_check_reserve(iqueue, sizeof(buf))) == 0 && 1652bc5531deSDag-Erling Smørgrav (r = sshbuf_check_reserve(oqueue, 1653bc5531deSDag-Erling Smørgrav SFTP_MAX_MSG_LENGTH)) == 0) 16541e8db6e2SBrian Feldman FD_SET(in, rset); 1655bc5531deSDag-Erling Smørgrav else if (r != SSH_ERR_NO_BUFFER_SPACE) 1656bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_check_reserve failed: %s", 1657bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1658d4af9e69SDag-Erling Smørgrav 1659bc5531deSDag-Erling Smørgrav olen = sshbuf_len(oqueue); 1660b66f2d16SKris Kennaway if (olen > 0) 16611e8db6e2SBrian Feldman FD_SET(out, wset); 1662b66f2d16SKris Kennaway 16631e8db6e2SBrian Feldman if (select(max+1, rset, wset, NULL, NULL) < 0) { 1664b66f2d16SKris Kennaway if (errno == EINTR) 1665b66f2d16SKris Kennaway continue; 1666761efaa7SDag-Erling Smørgrav error("select: %s", strerror(errno)); 1667d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(2); 1668b66f2d16SKris Kennaway } 1669b66f2d16SKris Kennaway 1670b66f2d16SKris Kennaway /* copy stdin to iqueue */ 16711e8db6e2SBrian Feldman if (FD_ISSET(in, rset)) { 1672b66f2d16SKris Kennaway len = read(in, buf, sizeof buf); 1673b66f2d16SKris Kennaway if (len == 0) { 1674b66f2d16SKris Kennaway debug("read eof"); 1675d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(0); 1676b66f2d16SKris Kennaway } else if (len < 0) { 1677761efaa7SDag-Erling Smørgrav error("read: %s", strerror(errno)); 1678d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1); 1679bc5531deSDag-Erling Smørgrav } else if ((r = sshbuf_put(iqueue, buf, len)) != 0) { 1680bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", 1681bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1682b66f2d16SKris Kennaway } 1683b66f2d16SKris Kennaway } 1684b66f2d16SKris Kennaway /* send oqueue to stdout */ 16851e8db6e2SBrian Feldman if (FD_ISSET(out, wset)) { 1686bc5531deSDag-Erling Smørgrav len = write(out, sshbuf_ptr(oqueue), olen); 1687b66f2d16SKris Kennaway if (len < 0) { 1688761efaa7SDag-Erling Smørgrav error("write: %s", strerror(errno)); 1689d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1); 1690bc5531deSDag-Erling Smørgrav } else if ((r = sshbuf_consume(oqueue, len)) != 0) { 1691bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", 1692bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1693b66f2d16SKris Kennaway } 1694b66f2d16SKris Kennaway } 1695d4af9e69SDag-Erling Smørgrav 1696d4af9e69SDag-Erling Smørgrav /* 1697d4af9e69SDag-Erling Smørgrav * Process requests from client if we can fit the results 1698d4af9e69SDag-Erling Smørgrav * into the output buffer, otherwise stop processing input 1699d4af9e69SDag-Erling Smørgrav * and let the output queue drain. 1700d4af9e69SDag-Erling Smørgrav */ 1701bc5531deSDag-Erling Smørgrav r = sshbuf_check_reserve(oqueue, SFTP_MAX_MSG_LENGTH); 1702bc5531deSDag-Erling Smørgrav if (r == 0) 1703b66f2d16SKris Kennaway process(); 1704bc5531deSDag-Erling Smørgrav else if (r != SSH_ERR_NO_BUFFER_SPACE) 1705bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_check_reserve: %s", 1706bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1707b66f2d16SKris Kennaway } 1708b66f2d16SKris Kennaway } 1709