1*acc1a9efSDag-Erling Smørgrav /* $OpenBSD: sftp-server.c,v 1.109 2016/02/15 09:47:49 dtucker 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 20bc5531deSDag-Erling Smørgrav #include <sys/param.h> /* MIN */ 21761efaa7SDag-Erling Smørgrav #include <sys/types.h> 22761efaa7SDag-Erling Smørgrav #include <sys/stat.h> 23761efaa7SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H 24761efaa7SDag-Erling Smørgrav # include <sys/time.h> 25761efaa7SDag-Erling Smørgrav #endif 26d4af9e69SDag-Erling Smørgrav #ifdef HAVE_SYS_MOUNT_H 27d4af9e69SDag-Erling Smørgrav #include <sys/mount.h> 28d4af9e69SDag-Erling Smørgrav #endif 29d4af9e69SDag-Erling Smørgrav #ifdef HAVE_SYS_STATVFS_H 30d4af9e69SDag-Erling Smørgrav #include <sys/statvfs.h> 31d4af9e69SDag-Erling Smørgrav #endif 32a0ee8cc6SDag-Erling Smørgrav #ifdef HAVE_SYS_PRCTL_H 33a0ee8cc6SDag-Erling Smørgrav #include <sys/prctl.h> 34a0ee8cc6SDag-Erling Smørgrav #endif 35761efaa7SDag-Erling Smørgrav 36761efaa7SDag-Erling Smørgrav #include <dirent.h> 37761efaa7SDag-Erling Smørgrav #include <errno.h> 38761efaa7SDag-Erling Smørgrav #include <fcntl.h> 39761efaa7SDag-Erling Smørgrav #include <pwd.h> 40761efaa7SDag-Erling Smørgrav #include <stdlib.h> 41761efaa7SDag-Erling Smørgrav #include <stdio.h> 42761efaa7SDag-Erling Smørgrav #include <string.h> 43761efaa7SDag-Erling Smørgrav #include <time.h> 44761efaa7SDag-Erling Smørgrav #include <unistd.h> 45761efaa7SDag-Erling Smørgrav #include <stdarg.h> 46761efaa7SDag-Erling Smørgrav 47b66f2d16SKris Kennaway #include "xmalloc.h" 48bc5531deSDag-Erling Smørgrav #include "sshbuf.h" 49bc5531deSDag-Erling Smørgrav #include "ssherr.h" 50761efaa7SDag-Erling Smørgrav #include "log.h" 51021d409fSDag-Erling Smørgrav #include "misc.h" 52f7167e0eSDag-Erling Smørgrav #include "match.h" 53761efaa7SDag-Erling Smørgrav #include "uidswap.h" 54b66f2d16SKris Kennaway 551e8db6e2SBrian Feldman #include "sftp.h" 561e8db6e2SBrian Feldman #include "sftp-common.h" 57b66f2d16SKris Kennaway 58761efaa7SDag-Erling Smørgrav /* Our verbosity */ 59f7167e0eSDag-Erling Smørgrav static LogLevel log_level = SYSLOG_LEVEL_ERROR; 60761efaa7SDag-Erling Smørgrav 61761efaa7SDag-Erling Smørgrav /* Our client */ 62f7167e0eSDag-Erling Smørgrav static struct passwd *pw = NULL; 63f7167e0eSDag-Erling Smørgrav static char *client_addr = NULL; 6483d2307dSDag-Erling Smørgrav 65b66f2d16SKris Kennaway /* input and output queue */ 66bc5531deSDag-Erling Smørgrav struct sshbuf *iqueue; 67bc5531deSDag-Erling Smørgrav struct sshbuf *oqueue; 68b66f2d16SKris Kennaway 691e8db6e2SBrian Feldman /* Version of client */ 70f7167e0eSDag-Erling Smørgrav static u_int version; 71f7167e0eSDag-Erling Smørgrav 72f7167e0eSDag-Erling Smørgrav /* SSH2_FXP_INIT received */ 73f7167e0eSDag-Erling Smørgrav static int init_done; 741e8db6e2SBrian Feldman 75b15c8340SDag-Erling Smørgrav /* Disable writes */ 76f7167e0eSDag-Erling Smørgrav static int readonly; 77f7167e0eSDag-Erling Smørgrav 78f7167e0eSDag-Erling Smørgrav /* Requests that are allowed/denied */ 79f7167e0eSDag-Erling Smørgrav static char *request_whitelist, *request_blacklist; 80b15c8340SDag-Erling Smørgrav 81d95e11bfSDag-Erling Smørgrav /* portable attributes, etc. */ 82b66f2d16SKris Kennaway typedef struct Stat Stat; 83b66f2d16SKris Kennaway 841e8db6e2SBrian Feldman struct Stat { 85b66f2d16SKris Kennaway char *name; 86b66f2d16SKris Kennaway char *long_name; 87b66f2d16SKris Kennaway Attrib attrib; 88b66f2d16SKris Kennaway }; 89b66f2d16SKris Kennaway 90f7167e0eSDag-Erling Smørgrav /* Packet handlers */ 91f7167e0eSDag-Erling Smørgrav static void process_open(u_int32_t id); 92f7167e0eSDag-Erling Smørgrav static void process_close(u_int32_t id); 93f7167e0eSDag-Erling Smørgrav static void process_read(u_int32_t id); 94f7167e0eSDag-Erling Smørgrav static void process_write(u_int32_t id); 95f7167e0eSDag-Erling Smørgrav static void process_stat(u_int32_t id); 96f7167e0eSDag-Erling Smørgrav static void process_lstat(u_int32_t id); 97f7167e0eSDag-Erling Smørgrav static void process_fstat(u_int32_t id); 98f7167e0eSDag-Erling Smørgrav static void process_setstat(u_int32_t id); 99f7167e0eSDag-Erling Smørgrav static void process_fsetstat(u_int32_t id); 100f7167e0eSDag-Erling Smørgrav static void process_opendir(u_int32_t id); 101f7167e0eSDag-Erling Smørgrav static void process_readdir(u_int32_t id); 102f7167e0eSDag-Erling Smørgrav static void process_remove(u_int32_t id); 103f7167e0eSDag-Erling Smørgrav static void process_mkdir(u_int32_t id); 104f7167e0eSDag-Erling Smørgrav static void process_rmdir(u_int32_t id); 105f7167e0eSDag-Erling Smørgrav static void process_realpath(u_int32_t id); 106f7167e0eSDag-Erling Smørgrav static void process_rename(u_int32_t id); 107f7167e0eSDag-Erling Smørgrav static void process_readlink(u_int32_t id); 108f7167e0eSDag-Erling Smørgrav static void process_symlink(u_int32_t id); 109f7167e0eSDag-Erling Smørgrav static void process_extended_posix_rename(u_int32_t id); 110f7167e0eSDag-Erling Smørgrav static void process_extended_statvfs(u_int32_t id); 111f7167e0eSDag-Erling Smørgrav static void process_extended_fstatvfs(u_int32_t id); 112f7167e0eSDag-Erling Smørgrav static void process_extended_hardlink(u_int32_t id); 113f7167e0eSDag-Erling Smørgrav static void process_extended_fsync(u_int32_t id); 114f7167e0eSDag-Erling Smørgrav static void process_extended(u_int32_t id); 115f7167e0eSDag-Erling Smørgrav 116f7167e0eSDag-Erling Smørgrav struct sftp_handler { 117f7167e0eSDag-Erling Smørgrav const char *name; /* user-visible name for fine-grained perms */ 118f7167e0eSDag-Erling Smørgrav const char *ext_name; /* extended request name */ 119f7167e0eSDag-Erling Smørgrav u_int type; /* packet type, for non extended packets */ 120f7167e0eSDag-Erling Smørgrav void (*handler)(u_int32_t); 121f7167e0eSDag-Erling Smørgrav int does_write; /* if nonzero, banned for readonly mode */ 122f7167e0eSDag-Erling Smørgrav }; 123f7167e0eSDag-Erling Smørgrav 124f7167e0eSDag-Erling Smørgrav struct sftp_handler handlers[] = { 125f7167e0eSDag-Erling Smørgrav /* NB. SSH2_FXP_OPEN does the readonly check in the handler itself */ 126f7167e0eSDag-Erling Smørgrav { "open", NULL, SSH2_FXP_OPEN, process_open, 0 }, 127f7167e0eSDag-Erling Smørgrav { "close", NULL, SSH2_FXP_CLOSE, process_close, 0 }, 128f7167e0eSDag-Erling Smørgrav { "read", NULL, SSH2_FXP_READ, process_read, 0 }, 129f7167e0eSDag-Erling Smørgrav { "write", NULL, SSH2_FXP_WRITE, process_write, 1 }, 130f7167e0eSDag-Erling Smørgrav { "lstat", NULL, SSH2_FXP_LSTAT, process_lstat, 0 }, 131f7167e0eSDag-Erling Smørgrav { "fstat", NULL, SSH2_FXP_FSTAT, process_fstat, 0 }, 132f7167e0eSDag-Erling Smørgrav { "setstat", NULL, SSH2_FXP_SETSTAT, process_setstat, 1 }, 133f7167e0eSDag-Erling Smørgrav { "fsetstat", NULL, SSH2_FXP_FSETSTAT, process_fsetstat, 1 }, 134f7167e0eSDag-Erling Smørgrav { "opendir", NULL, SSH2_FXP_OPENDIR, process_opendir, 0 }, 135f7167e0eSDag-Erling Smørgrav { "readdir", NULL, SSH2_FXP_READDIR, process_readdir, 0 }, 136f7167e0eSDag-Erling Smørgrav { "remove", NULL, SSH2_FXP_REMOVE, process_remove, 1 }, 137f7167e0eSDag-Erling Smørgrav { "mkdir", NULL, SSH2_FXP_MKDIR, process_mkdir, 1 }, 138f7167e0eSDag-Erling Smørgrav { "rmdir", NULL, SSH2_FXP_RMDIR, process_rmdir, 1 }, 139f7167e0eSDag-Erling Smørgrav { "realpath", NULL, SSH2_FXP_REALPATH, process_realpath, 0 }, 140f7167e0eSDag-Erling Smørgrav { "stat", NULL, SSH2_FXP_STAT, process_stat, 0 }, 141f7167e0eSDag-Erling Smørgrav { "rename", NULL, SSH2_FXP_RENAME, process_rename, 1 }, 142f7167e0eSDag-Erling Smørgrav { "readlink", NULL, SSH2_FXP_READLINK, process_readlink, 0 }, 143f7167e0eSDag-Erling Smørgrav { "symlink", NULL, SSH2_FXP_SYMLINK, process_symlink, 1 }, 144f7167e0eSDag-Erling Smørgrav { NULL, NULL, 0, NULL, 0 } 145f7167e0eSDag-Erling Smørgrav }; 146f7167e0eSDag-Erling Smørgrav 147f7167e0eSDag-Erling Smørgrav /* SSH2_FXP_EXTENDED submessages */ 148f7167e0eSDag-Erling Smørgrav struct sftp_handler extended_handlers[] = { 149f7167e0eSDag-Erling Smørgrav { "posix-rename", "posix-rename@openssh.com", 0, 150f7167e0eSDag-Erling Smørgrav process_extended_posix_rename, 1 }, 151f7167e0eSDag-Erling Smørgrav { "statvfs", "statvfs@openssh.com", 0, process_extended_statvfs, 0 }, 152f7167e0eSDag-Erling Smørgrav { "fstatvfs", "fstatvfs@openssh.com", 0, process_extended_fstatvfs, 0 }, 153f7167e0eSDag-Erling Smørgrav { "hardlink", "hardlink@openssh.com", 0, process_extended_hardlink, 1 }, 154f7167e0eSDag-Erling Smørgrav { "fsync", "fsync@openssh.com", 0, process_extended_fsync, 1 }, 155f7167e0eSDag-Erling Smørgrav { NULL, NULL, 0, NULL, 0 } 156f7167e0eSDag-Erling Smørgrav }; 157f7167e0eSDag-Erling Smørgrav 158f7167e0eSDag-Erling Smørgrav static int 159f7167e0eSDag-Erling Smørgrav request_permitted(struct sftp_handler *h) 160f7167e0eSDag-Erling Smørgrav { 161f7167e0eSDag-Erling Smørgrav char *result; 162f7167e0eSDag-Erling Smørgrav 163f7167e0eSDag-Erling Smørgrav if (readonly && h->does_write) { 164f7167e0eSDag-Erling Smørgrav verbose("Refusing %s request in read-only mode", h->name); 165f7167e0eSDag-Erling Smørgrav return 0; 166f7167e0eSDag-Erling Smørgrav } 167f7167e0eSDag-Erling Smørgrav if (request_blacklist != NULL && 168f7167e0eSDag-Erling Smørgrav ((result = match_list(h->name, request_blacklist, NULL))) != NULL) { 169f7167e0eSDag-Erling Smørgrav free(result); 170f7167e0eSDag-Erling Smørgrav verbose("Refusing blacklisted %s request", h->name); 171f7167e0eSDag-Erling Smørgrav return 0; 172f7167e0eSDag-Erling Smørgrav } 173f7167e0eSDag-Erling Smørgrav if (request_whitelist != NULL && 174f7167e0eSDag-Erling Smørgrav ((result = match_list(h->name, request_whitelist, NULL))) != NULL) { 175f7167e0eSDag-Erling Smørgrav free(result); 176f7167e0eSDag-Erling Smørgrav debug2("Permitting whitelisted %s request", h->name); 177f7167e0eSDag-Erling Smørgrav return 1; 178f7167e0eSDag-Erling Smørgrav } 179f7167e0eSDag-Erling Smørgrav if (request_whitelist != NULL) { 180f7167e0eSDag-Erling Smørgrav verbose("Refusing non-whitelisted %s request", h->name); 181f7167e0eSDag-Erling Smørgrav return 0; 182f7167e0eSDag-Erling Smørgrav } 183f7167e0eSDag-Erling Smørgrav return 1; 184f7167e0eSDag-Erling Smørgrav } 185f7167e0eSDag-Erling Smørgrav 186ae1f160dSDag-Erling Smørgrav static int 187b66f2d16SKris Kennaway errno_to_portable(int unixerrno) 188b66f2d16SKris Kennaway { 189b66f2d16SKris Kennaway int ret = 0; 1901e8db6e2SBrian Feldman 191b66f2d16SKris Kennaway switch (unixerrno) { 192b66f2d16SKris Kennaway case 0: 1931e8db6e2SBrian Feldman ret = SSH2_FX_OK; 194b66f2d16SKris Kennaway break; 195b66f2d16SKris Kennaway case ENOENT: 196b66f2d16SKris Kennaway case ENOTDIR: 197b66f2d16SKris Kennaway case EBADF: 198b66f2d16SKris Kennaway case ELOOP: 1991e8db6e2SBrian Feldman ret = SSH2_FX_NO_SUCH_FILE; 200b66f2d16SKris Kennaway break; 201b66f2d16SKris Kennaway case EPERM: 202b66f2d16SKris Kennaway case EACCES: 203b66f2d16SKris Kennaway case EFAULT: 2041e8db6e2SBrian Feldman ret = SSH2_FX_PERMISSION_DENIED; 205b66f2d16SKris Kennaway break; 206b66f2d16SKris Kennaway case ENAMETOOLONG: 207b66f2d16SKris Kennaway case EINVAL: 2081e8db6e2SBrian Feldman ret = SSH2_FX_BAD_MESSAGE; 209b66f2d16SKris Kennaway break; 210d4af9e69SDag-Erling Smørgrav case ENOSYS: 211d4af9e69SDag-Erling Smørgrav ret = SSH2_FX_OP_UNSUPPORTED; 212d4af9e69SDag-Erling Smørgrav break; 213b66f2d16SKris Kennaway default: 2141e8db6e2SBrian Feldman ret = SSH2_FX_FAILURE; 215b66f2d16SKris Kennaway break; 216b66f2d16SKris Kennaway } 217b66f2d16SKris Kennaway return ret; 218b66f2d16SKris Kennaway } 219b66f2d16SKris Kennaway 220ae1f160dSDag-Erling Smørgrav static int 221b66f2d16SKris Kennaway flags_from_portable(int pflags) 222b66f2d16SKris Kennaway { 223b66f2d16SKris Kennaway int flags = 0; 2241e8db6e2SBrian Feldman 2251e8db6e2SBrian Feldman if ((pflags & SSH2_FXF_READ) && 2261e8db6e2SBrian Feldman (pflags & SSH2_FXF_WRITE)) { 227b66f2d16SKris Kennaway flags = O_RDWR; 2281e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_READ) { 229b66f2d16SKris Kennaway flags = O_RDONLY; 2301e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_WRITE) { 231b66f2d16SKris Kennaway flags = O_WRONLY; 232b66f2d16SKris Kennaway } 233f7167e0eSDag-Erling Smørgrav if (pflags & SSH2_FXF_APPEND) 234f7167e0eSDag-Erling Smørgrav flags |= O_APPEND; 2351e8db6e2SBrian Feldman if (pflags & SSH2_FXF_CREAT) 236b66f2d16SKris Kennaway flags |= O_CREAT; 2371e8db6e2SBrian Feldman if (pflags & SSH2_FXF_TRUNC) 238b66f2d16SKris Kennaway flags |= O_TRUNC; 2391e8db6e2SBrian Feldman if (pflags & SSH2_FXF_EXCL) 240b66f2d16SKris Kennaway flags |= O_EXCL; 241b66f2d16SKris Kennaway return flags; 242b66f2d16SKris Kennaway } 243b66f2d16SKris Kennaway 244761efaa7SDag-Erling Smørgrav static const char * 245761efaa7SDag-Erling Smørgrav string_from_portable(int pflags) 246761efaa7SDag-Erling Smørgrav { 247761efaa7SDag-Erling Smørgrav static char ret[128]; 248761efaa7SDag-Erling Smørgrav 249761efaa7SDag-Erling Smørgrav *ret = '\0'; 250761efaa7SDag-Erling Smørgrav 251761efaa7SDag-Erling Smørgrav #define PAPPEND(str) { \ 252761efaa7SDag-Erling Smørgrav if (*ret != '\0') \ 253761efaa7SDag-Erling Smørgrav strlcat(ret, ",", sizeof(ret)); \ 254761efaa7SDag-Erling Smørgrav strlcat(ret, str, sizeof(ret)); \ 255761efaa7SDag-Erling Smørgrav } 256761efaa7SDag-Erling Smørgrav 257761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_READ) 258761efaa7SDag-Erling Smørgrav PAPPEND("READ") 259761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_WRITE) 260761efaa7SDag-Erling Smørgrav PAPPEND("WRITE") 261f7167e0eSDag-Erling Smørgrav if (pflags & SSH2_FXF_APPEND) 262f7167e0eSDag-Erling Smørgrav PAPPEND("APPEND") 263761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_CREAT) 264761efaa7SDag-Erling Smørgrav PAPPEND("CREATE") 265761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_TRUNC) 266761efaa7SDag-Erling Smørgrav PAPPEND("TRUNCATE") 267761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_EXCL) 268761efaa7SDag-Erling Smørgrav PAPPEND("EXCL") 269761efaa7SDag-Erling Smørgrav 270761efaa7SDag-Erling Smørgrav return ret; 271761efaa7SDag-Erling Smørgrav } 272761efaa7SDag-Erling Smørgrav 273b66f2d16SKris Kennaway /* handle handles */ 274b66f2d16SKris Kennaway 275b66f2d16SKris Kennaway typedef struct Handle Handle; 276b66f2d16SKris Kennaway struct Handle { 277b66f2d16SKris Kennaway int use; 278b66f2d16SKris Kennaway DIR *dirp; 279b66f2d16SKris Kennaway int fd; 280f7167e0eSDag-Erling Smørgrav int flags; 281b66f2d16SKris Kennaway char *name; 282761efaa7SDag-Erling Smørgrav u_int64_t bytes_read, bytes_write; 283d4af9e69SDag-Erling Smørgrav int next_unused; 284b66f2d16SKris Kennaway }; 2851e8db6e2SBrian Feldman 286b66f2d16SKris Kennaway enum { 287b66f2d16SKris Kennaway HANDLE_UNUSED, 288b66f2d16SKris Kennaway HANDLE_DIR, 289b66f2d16SKris Kennaway HANDLE_FILE 290b66f2d16SKris Kennaway }; 2911e8db6e2SBrian Feldman 292d4af9e69SDag-Erling Smørgrav Handle *handles = NULL; 293d4af9e69SDag-Erling Smørgrav u_int num_handles = 0; 294d4af9e69SDag-Erling Smørgrav int first_unused_handle = -1; 295b66f2d16SKris Kennaway 296d4af9e69SDag-Erling Smørgrav static void handle_unused(int i) 297b66f2d16SKris Kennaway { 298b66f2d16SKris Kennaway handles[i].use = HANDLE_UNUSED; 299d4af9e69SDag-Erling Smørgrav handles[i].next_unused = first_unused_handle; 300d4af9e69SDag-Erling Smørgrav first_unused_handle = i; 301b66f2d16SKris Kennaway } 302b66f2d16SKris Kennaway 303ae1f160dSDag-Erling Smørgrav static int 304f7167e0eSDag-Erling Smørgrav handle_new(int use, const char *name, int fd, int flags, DIR *dirp) 305b66f2d16SKris Kennaway { 306d4af9e69SDag-Erling Smørgrav int i; 3071e8db6e2SBrian Feldman 308d4af9e69SDag-Erling Smørgrav if (first_unused_handle == -1) { 309d4af9e69SDag-Erling Smørgrav if (num_handles + 1 <= num_handles) 310d4af9e69SDag-Erling Smørgrav return -1; 311d4af9e69SDag-Erling Smørgrav num_handles++; 312557f75e5SDag-Erling Smørgrav handles = xreallocarray(handles, num_handles, sizeof(Handle)); 313d4af9e69SDag-Erling Smørgrav handle_unused(num_handles - 1); 314d4af9e69SDag-Erling Smørgrav } 315d4af9e69SDag-Erling Smørgrav 316d4af9e69SDag-Erling Smørgrav i = first_unused_handle; 317d4af9e69SDag-Erling Smørgrav first_unused_handle = handles[i].next_unused; 318d4af9e69SDag-Erling Smørgrav 319b66f2d16SKris Kennaway handles[i].use = use; 320b66f2d16SKris Kennaway handles[i].dirp = dirp; 321b66f2d16SKris Kennaway handles[i].fd = fd; 322f7167e0eSDag-Erling Smørgrav handles[i].flags = flags; 323d0c8c0bcSDag-Erling Smørgrav handles[i].name = xstrdup(name); 324761efaa7SDag-Erling Smørgrav handles[i].bytes_read = handles[i].bytes_write = 0; 325d4af9e69SDag-Erling Smørgrav 326b66f2d16SKris Kennaway return i; 327b66f2d16SKris Kennaway } 328b66f2d16SKris Kennaway 329ae1f160dSDag-Erling Smørgrav static int 330b66f2d16SKris Kennaway handle_is_ok(int i, int type) 331b66f2d16SKris Kennaway { 332d4af9e69SDag-Erling Smørgrav return i >= 0 && (u_int)i < num_handles && handles[i].use == type; 333b66f2d16SKris Kennaway } 334b66f2d16SKris Kennaway 335ae1f160dSDag-Erling Smørgrav static int 336bc5531deSDag-Erling Smørgrav handle_to_string(int handle, u_char **stringp, int *hlenp) 337b66f2d16SKris Kennaway { 338b66f2d16SKris Kennaway if (stringp == NULL || hlenp == NULL) 339b66f2d16SKris Kennaway return -1; 3401e8db6e2SBrian Feldman *stringp = xmalloc(sizeof(int32_t)); 341761efaa7SDag-Erling Smørgrav put_u32(*stringp, handle); 3421e8db6e2SBrian Feldman *hlenp = sizeof(int32_t); 343b66f2d16SKris Kennaway return 0; 344b66f2d16SKris Kennaway } 345b66f2d16SKris Kennaway 346ae1f160dSDag-Erling Smørgrav static int 347bc5531deSDag-Erling Smørgrav handle_from_string(const u_char *handle, u_int hlen) 348b66f2d16SKris Kennaway { 3491e8db6e2SBrian Feldman int val; 3501e8db6e2SBrian Feldman 3511e8db6e2SBrian Feldman if (hlen != sizeof(int32_t)) 352b66f2d16SKris Kennaway return -1; 353761efaa7SDag-Erling Smørgrav val = get_u32(handle); 354b66f2d16SKris Kennaway if (handle_is_ok(val, HANDLE_FILE) || 355b66f2d16SKris Kennaway handle_is_ok(val, HANDLE_DIR)) 356b66f2d16SKris Kennaway return val; 357b66f2d16SKris Kennaway return -1; 358b66f2d16SKris Kennaway } 359b66f2d16SKris Kennaway 360ae1f160dSDag-Erling Smørgrav static char * 361b66f2d16SKris Kennaway handle_to_name(int handle) 362b66f2d16SKris Kennaway { 363b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)|| 364b66f2d16SKris Kennaway handle_is_ok(handle, HANDLE_FILE)) 365b66f2d16SKris Kennaway return handles[handle].name; 366b66f2d16SKris Kennaway return NULL; 367b66f2d16SKris Kennaway } 368b66f2d16SKris Kennaway 369ae1f160dSDag-Erling Smørgrav static DIR * 370b66f2d16SKris Kennaway handle_to_dir(int handle) 371b66f2d16SKris Kennaway { 372b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)) 373b66f2d16SKris Kennaway return handles[handle].dirp; 374b66f2d16SKris Kennaway return NULL; 375b66f2d16SKris Kennaway } 376b66f2d16SKris Kennaway 377ae1f160dSDag-Erling Smørgrav static int 378b66f2d16SKris Kennaway handle_to_fd(int handle) 379b66f2d16SKris Kennaway { 380b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) 381b66f2d16SKris Kennaway return handles[handle].fd; 382b66f2d16SKris Kennaway return -1; 383b66f2d16SKris Kennaway } 384b66f2d16SKris Kennaway 385f7167e0eSDag-Erling Smørgrav static int 386f7167e0eSDag-Erling Smørgrav handle_to_flags(int handle) 387f7167e0eSDag-Erling Smørgrav { 388f7167e0eSDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 389f7167e0eSDag-Erling Smørgrav return handles[handle].flags; 390f7167e0eSDag-Erling Smørgrav return 0; 391f7167e0eSDag-Erling Smørgrav } 392f7167e0eSDag-Erling Smørgrav 393761efaa7SDag-Erling Smørgrav static void 394761efaa7SDag-Erling Smørgrav handle_update_read(int handle, ssize_t bytes) 395761efaa7SDag-Erling Smørgrav { 396761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 397761efaa7SDag-Erling Smørgrav handles[handle].bytes_read += bytes; 398761efaa7SDag-Erling Smørgrav } 399761efaa7SDag-Erling Smørgrav 400761efaa7SDag-Erling Smørgrav static void 401761efaa7SDag-Erling Smørgrav handle_update_write(int handle, ssize_t bytes) 402761efaa7SDag-Erling Smørgrav { 403761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 404761efaa7SDag-Erling Smørgrav handles[handle].bytes_write += bytes; 405761efaa7SDag-Erling Smørgrav } 406761efaa7SDag-Erling Smørgrav 407761efaa7SDag-Erling Smørgrav static u_int64_t 408761efaa7SDag-Erling Smørgrav handle_bytes_read(int handle) 409761efaa7SDag-Erling Smørgrav { 410761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 411761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_read); 412761efaa7SDag-Erling Smørgrav return 0; 413761efaa7SDag-Erling Smørgrav } 414761efaa7SDag-Erling Smørgrav 415761efaa7SDag-Erling Smørgrav static u_int64_t 416761efaa7SDag-Erling Smørgrav handle_bytes_write(int handle) 417761efaa7SDag-Erling Smørgrav { 418761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 419761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_write); 420761efaa7SDag-Erling Smørgrav return 0; 421761efaa7SDag-Erling Smørgrav } 422761efaa7SDag-Erling Smørgrav 423ae1f160dSDag-Erling Smørgrav static int 424b66f2d16SKris Kennaway handle_close(int handle) 425b66f2d16SKris Kennaway { 426b66f2d16SKris Kennaway int ret = -1; 4271e8db6e2SBrian Feldman 428b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) { 429b66f2d16SKris Kennaway ret = close(handles[handle].fd); 430e4a9863fSDag-Erling Smørgrav free(handles[handle].name); 431d4af9e69SDag-Erling Smørgrav handle_unused(handle); 432b66f2d16SKris Kennaway } else if (handle_is_ok(handle, HANDLE_DIR)) { 433b66f2d16SKris Kennaway ret = closedir(handles[handle].dirp); 434e4a9863fSDag-Erling Smørgrav free(handles[handle].name); 435d4af9e69SDag-Erling Smørgrav handle_unused(handle); 436b66f2d16SKris Kennaway } else { 437b66f2d16SKris Kennaway errno = ENOENT; 438b66f2d16SKris Kennaway } 439b66f2d16SKris Kennaway return ret; 440b66f2d16SKris Kennaway } 441b66f2d16SKris Kennaway 442761efaa7SDag-Erling Smørgrav static void 443761efaa7SDag-Erling Smørgrav handle_log_close(int handle, char *emsg) 444761efaa7SDag-Erling Smørgrav { 445761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) { 446761efaa7SDag-Erling Smørgrav logit("%s%sclose \"%s\" bytes read %llu written %llu", 447761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 448761efaa7SDag-Erling Smørgrav handle_to_name(handle), 449d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_read(handle), 450d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_write(handle)); 451761efaa7SDag-Erling Smørgrav } else { 452761efaa7SDag-Erling Smørgrav logit("%s%sclosedir \"%s\"", 453761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 454761efaa7SDag-Erling Smørgrav handle_to_name(handle)); 455761efaa7SDag-Erling Smørgrav } 456761efaa7SDag-Erling Smørgrav } 457761efaa7SDag-Erling Smørgrav 458761efaa7SDag-Erling Smørgrav static void 459761efaa7SDag-Erling Smørgrav handle_log_exit(void) 460761efaa7SDag-Erling Smørgrav { 461761efaa7SDag-Erling Smørgrav u_int i; 462761efaa7SDag-Erling Smørgrav 463d4af9e69SDag-Erling Smørgrav for (i = 0; i < num_handles; i++) 464761efaa7SDag-Erling Smørgrav if (handles[i].use != HANDLE_UNUSED) 465761efaa7SDag-Erling Smørgrav handle_log_close(i, "forced"); 466761efaa7SDag-Erling Smørgrav } 467761efaa7SDag-Erling Smørgrav 468ae1f160dSDag-Erling Smørgrav static int 469bc5531deSDag-Erling Smørgrav get_handle(struct sshbuf *queue, int *hp) 470b66f2d16SKris Kennaway { 471bc5531deSDag-Erling Smørgrav u_char *handle; 472bc5531deSDag-Erling Smørgrav int r; 473bc5531deSDag-Erling Smørgrav size_t hlen; 4741e8db6e2SBrian Feldman 475bc5531deSDag-Erling Smørgrav *hp = -1; 476bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_string(queue, &handle, &hlen)) != 0) 477bc5531deSDag-Erling Smørgrav return r; 4781e8db6e2SBrian Feldman if (hlen < 256) 479bc5531deSDag-Erling Smørgrav *hp = handle_from_string(handle, hlen); 480e4a9863fSDag-Erling Smørgrav free(handle); 481bc5531deSDag-Erling Smørgrav return 0; 482b66f2d16SKris Kennaway } 483b66f2d16SKris Kennaway 484b66f2d16SKris Kennaway /* send replies */ 485b66f2d16SKris Kennaway 486ae1f160dSDag-Erling Smørgrav static void 487bc5531deSDag-Erling Smørgrav send_msg(struct sshbuf *m) 488b66f2d16SKris Kennaway { 489bc5531deSDag-Erling Smørgrav int r; 4901e8db6e2SBrian Feldman 491bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_stringb(oqueue, m)) != 0) 492bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 493bc5531deSDag-Erling Smørgrav sshbuf_reset(m); 494b66f2d16SKris Kennaway } 495b66f2d16SKris Kennaway 496761efaa7SDag-Erling Smørgrav static const char * 497761efaa7SDag-Erling Smørgrav status_to_message(u_int32_t status) 498b66f2d16SKris Kennaway { 4991e8db6e2SBrian Feldman const char *status_messages[] = { 5001e8db6e2SBrian Feldman "Success", /* SSH_FX_OK */ 5011e8db6e2SBrian Feldman "End of file", /* SSH_FX_EOF */ 5021e8db6e2SBrian Feldman "No such file", /* SSH_FX_NO_SUCH_FILE */ 5031e8db6e2SBrian Feldman "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 5041e8db6e2SBrian Feldman "Failure", /* SSH_FX_FAILURE */ 5051e8db6e2SBrian Feldman "Bad message", /* SSH_FX_BAD_MESSAGE */ 5061e8db6e2SBrian Feldman "No connection", /* SSH_FX_NO_CONNECTION */ 5071e8db6e2SBrian Feldman "Connection lost", /* SSH_FX_CONNECTION_LOST */ 5081e8db6e2SBrian Feldman "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 5091e8db6e2SBrian Feldman "Unknown error" /* Others */ 5101e8db6e2SBrian Feldman }; 511761efaa7SDag-Erling Smørgrav return (status_messages[MIN(status,SSH2_FX_MAX)]); 512761efaa7SDag-Erling Smørgrav } 5131e8db6e2SBrian Feldman 514761efaa7SDag-Erling Smørgrav static void 515761efaa7SDag-Erling Smørgrav send_status(u_int32_t id, u_int32_t status) 516761efaa7SDag-Erling Smørgrav { 517bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 518bc5531deSDag-Erling Smørgrav int r; 519761efaa7SDag-Erling Smørgrav 520761efaa7SDag-Erling Smørgrav debug3("request %u: sent status %u", id, status); 521761efaa7SDag-Erling Smørgrav if (log_level > SYSLOG_LEVEL_VERBOSE || 522761efaa7SDag-Erling Smørgrav (status != SSH2_FX_OK && status != SSH2_FX_EOF)) 523761efaa7SDag-Erling Smørgrav logit("sent status %s", status_to_message(status)); 524bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 525bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 526bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_STATUS)) != 0 || 527bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 528bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, status)) != 0) 529bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 5301e8db6e2SBrian Feldman if (version >= 3) { 531bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(msg, 532bc5531deSDag-Erling Smørgrav status_to_message(status))) != 0 || 533bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "")) != 0) 534bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 5351e8db6e2SBrian Feldman } 536bc5531deSDag-Erling Smørgrav send_msg(msg); 537bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 538b66f2d16SKris Kennaway } 539ae1f160dSDag-Erling Smørgrav static void 540bc5531deSDag-Erling Smørgrav send_data_or_handle(char type, u_int32_t id, const u_char *data, int dlen) 541b66f2d16SKris Kennaway { 542bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 543bc5531deSDag-Erling Smørgrav int r; 5441e8db6e2SBrian Feldman 545bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 546bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 547bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, type)) != 0 || 548bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 549bc5531deSDag-Erling Smørgrav (r = sshbuf_put_string(msg, data, dlen)) != 0) 550bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 551bc5531deSDag-Erling Smørgrav send_msg(msg); 552bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 553b66f2d16SKris Kennaway } 554b66f2d16SKris Kennaway 555ae1f160dSDag-Erling Smørgrav static void 556bc5531deSDag-Erling Smørgrav send_data(u_int32_t id, const u_char *data, int dlen) 557b66f2d16SKris Kennaway { 558761efaa7SDag-Erling Smørgrav debug("request %u: sent data len %d", id, dlen); 5591e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 560b66f2d16SKris Kennaway } 561b66f2d16SKris Kennaway 562ae1f160dSDag-Erling Smørgrav static void 563b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle) 564b66f2d16SKris Kennaway { 565bc5531deSDag-Erling Smørgrav u_char *string; 566b66f2d16SKris Kennaway int hlen; 5671e8db6e2SBrian Feldman 568b66f2d16SKris Kennaway handle_to_string(handle, &string, &hlen); 569761efaa7SDag-Erling Smørgrav debug("request %u: sent handle handle %d", id, handle); 5701e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 571e4a9863fSDag-Erling Smørgrav free(string); 572b66f2d16SKris Kennaway } 573b66f2d16SKris Kennaway 574ae1f160dSDag-Erling Smørgrav static void 575efcad6b7SDag-Erling Smørgrav send_names(u_int32_t id, int count, const Stat *stats) 576b66f2d16SKris Kennaway { 577bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 578bc5531deSDag-Erling Smørgrav int i, r; 5791e8db6e2SBrian Feldman 580bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 581bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 582bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_NAME)) != 0 || 583bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 584bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, count)) != 0) 585bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 586761efaa7SDag-Erling Smørgrav debug("request %u: sent names count %d", id, count); 587b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 588bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(msg, stats[i].name)) != 0 || 589bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, stats[i].long_name)) != 0 || 590bc5531deSDag-Erling Smørgrav (r = encode_attrib(msg, &stats[i].attrib)) != 0) 591bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 592b66f2d16SKris Kennaway } 593bc5531deSDag-Erling Smørgrav send_msg(msg); 594bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 595b66f2d16SKris Kennaway } 596b66f2d16SKris Kennaway 597ae1f160dSDag-Erling Smørgrav static void 598efcad6b7SDag-Erling Smørgrav send_attrib(u_int32_t id, const Attrib *a) 599b66f2d16SKris Kennaway { 600bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 601bc5531deSDag-Erling Smørgrav int r; 6021e8db6e2SBrian Feldman 603761efaa7SDag-Erling Smørgrav debug("request %u: sent attrib have 0x%x", id, a->flags); 604bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 605bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 606bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_ATTRS)) != 0 || 607bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 608bc5531deSDag-Erling Smørgrav (r = encode_attrib(msg, a)) != 0) 609bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 610bc5531deSDag-Erling Smørgrav send_msg(msg); 611bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 612b66f2d16SKris Kennaway } 613b66f2d16SKris Kennaway 614d4af9e69SDag-Erling Smørgrav static void 615d4af9e69SDag-Erling Smørgrav send_statvfs(u_int32_t id, struct statvfs *st) 616d4af9e69SDag-Erling Smørgrav { 617bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 618d4af9e69SDag-Erling Smørgrav u_int64_t flag; 619bc5531deSDag-Erling Smørgrav int r; 620d4af9e69SDag-Erling Smørgrav 621d4af9e69SDag-Erling Smørgrav flag = (st->f_flag & ST_RDONLY) ? SSH2_FXE_STATVFS_ST_RDONLY : 0; 622d4af9e69SDag-Erling Smørgrav flag |= (st->f_flag & ST_NOSUID) ? SSH2_FXE_STATVFS_ST_NOSUID : 0; 623d4af9e69SDag-Erling Smørgrav 624bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 625bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 626bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED_REPLY)) != 0 || 627bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 628bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_bsize)) != 0 || 629bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_frsize)) != 0 || 630bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_blocks)) != 0 || 631bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_bfree)) != 0 || 632bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_bavail)) != 0 || 633bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_files)) != 0 || 634bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_ffree)) != 0 || 635bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_favail)) != 0 || 636bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, FSID_TO_ULONG(st->f_fsid))) != 0 || 637bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, flag)) != 0 || 638bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_namemax)) != 0) 639bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 640bc5531deSDag-Erling Smørgrav send_msg(msg); 641bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 642d4af9e69SDag-Erling Smørgrav } 643d4af9e69SDag-Erling Smørgrav 644b66f2d16SKris Kennaway /* parse incoming */ 645b66f2d16SKris Kennaway 646ae1f160dSDag-Erling Smørgrav static void 647b66f2d16SKris Kennaway process_init(void) 648b66f2d16SKris Kennaway { 649bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 650bc5531deSDag-Erling Smørgrav int r; 651b66f2d16SKris Kennaway 652bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(iqueue, &version)) != 0) 653bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 654e146993eSDag-Erling Smørgrav verbose("received client version %u", version); 655bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 656bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 657bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_VERSION)) != 0 || 658bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, SSH2_FILEXFER_VERSION)) != 0 || 659d4af9e69SDag-Erling Smørgrav /* POSIX rename extension */ 660bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "posix-rename@openssh.com")) != 0 || 661bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "1")) != 0 || /* version */ 662d4af9e69SDag-Erling Smørgrav /* statvfs extension */ 663bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "statvfs@openssh.com")) != 0 || 664bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "2")) != 0 || /* version */ 665d4af9e69SDag-Erling Smørgrav /* fstatvfs extension */ 666bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "fstatvfs@openssh.com")) != 0 || 667bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "2")) != 0 || /* version */ 6684a421b63SDag-Erling Smørgrav /* hardlink extension */ 669bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "hardlink@openssh.com")) != 0 || 670bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "1")) != 0 || /* version */ 671f7167e0eSDag-Erling Smørgrav /* fsync extension */ 672bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "fsync@openssh.com")) != 0 || 673bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "1")) != 0) /* version */ 674bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 675bc5531deSDag-Erling Smørgrav send_msg(msg); 676bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 677b66f2d16SKris Kennaway } 678b66f2d16SKris Kennaway 679ae1f160dSDag-Erling Smørgrav static void 680f7167e0eSDag-Erling Smørgrav process_open(u_int32_t id) 681b66f2d16SKris Kennaway { 682f7167e0eSDag-Erling Smørgrav u_int32_t pflags; 683bc5531deSDag-Erling Smørgrav Attrib a; 684b66f2d16SKris Kennaway char *name; 685bc5531deSDag-Erling Smørgrav int r, handle, fd, flags, mode, status = SSH2_FX_FAILURE; 686b66f2d16SKris Kennaway 687bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 688bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u32(iqueue, &pflags)) != 0 || /* portable flags */ 689bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 690bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 691bc5531deSDag-Erling Smørgrav 692761efaa7SDag-Erling Smørgrav debug3("request %u: open flags %d", id, pflags); 693b66f2d16SKris Kennaway flags = flags_from_portable(pflags); 694bc5531deSDag-Erling Smørgrav mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a.perm : 0666; 695761efaa7SDag-Erling Smørgrav logit("open \"%s\" flags %s mode 0%o", 696761efaa7SDag-Erling Smørgrav name, string_from_portable(pflags), mode); 697b15c8340SDag-Erling Smørgrav if (readonly && 698f7167e0eSDag-Erling Smørgrav ((flags & O_ACCMODE) == O_WRONLY || 699f7167e0eSDag-Erling Smørgrav (flags & O_ACCMODE) == O_RDWR)) { 700f7167e0eSDag-Erling Smørgrav verbose("Refusing open request in read-only mode"); 701b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 702f7167e0eSDag-Erling Smørgrav } else { 703b66f2d16SKris Kennaway fd = open(name, flags, mode); 704b66f2d16SKris Kennaway if (fd < 0) { 705b66f2d16SKris Kennaway status = errno_to_portable(errno); 706b66f2d16SKris Kennaway } else { 707f7167e0eSDag-Erling Smørgrav handle = handle_new(HANDLE_FILE, name, fd, flags, NULL); 708b66f2d16SKris Kennaway if (handle < 0) { 709b66f2d16SKris Kennaway close(fd); 710b66f2d16SKris Kennaway } else { 711b66f2d16SKris Kennaway send_handle(id, handle); 7121e8db6e2SBrian Feldman status = SSH2_FX_OK; 713b66f2d16SKris Kennaway } 714b66f2d16SKris Kennaway } 715b15c8340SDag-Erling Smørgrav } 7161e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 717b66f2d16SKris Kennaway send_status(id, status); 718e4a9863fSDag-Erling Smørgrav free(name); 719b66f2d16SKris Kennaway } 720b66f2d16SKris Kennaway 721ae1f160dSDag-Erling Smørgrav static void 722f7167e0eSDag-Erling Smørgrav process_close(u_int32_t id) 723b66f2d16SKris Kennaway { 724bc5531deSDag-Erling Smørgrav int r, handle, ret, status = SSH2_FX_FAILURE; 725b66f2d16SKris Kennaway 726bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 727bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 728bc5531deSDag-Erling Smørgrav 729761efaa7SDag-Erling Smørgrav debug3("request %u: close handle %u", id, handle); 730761efaa7SDag-Erling Smørgrav handle_log_close(handle, NULL); 731b66f2d16SKris Kennaway ret = handle_close(handle); 7321e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 733b66f2d16SKris Kennaway send_status(id, status); 734b66f2d16SKris Kennaway } 735b66f2d16SKris Kennaway 736ae1f160dSDag-Erling Smørgrav static void 737f7167e0eSDag-Erling Smørgrav process_read(u_int32_t id) 738b66f2d16SKris Kennaway { 739bc5531deSDag-Erling Smørgrav u_char buf[64*1024]; 740f7167e0eSDag-Erling Smørgrav u_int32_t len; 741bc5531deSDag-Erling Smørgrav int r, handle, fd, ret, status = SSH2_FX_FAILURE; 742b66f2d16SKris Kennaway u_int64_t off; 743b66f2d16SKris Kennaway 744bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0 || 745bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u64(iqueue, &off)) != 0 || 746bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u32(iqueue, &len)) != 0) 747bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 748b66f2d16SKris Kennaway 749761efaa7SDag-Erling Smørgrav debug("request %u: read \"%s\" (handle %d) off %llu len %d", 750761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len); 751b66f2d16SKris Kennaway if (len > sizeof buf) { 752b66f2d16SKris Kennaway len = sizeof buf; 753761efaa7SDag-Erling Smørgrav debug2("read change len %d", len); 754b66f2d16SKris Kennaway } 755b66f2d16SKris Kennaway fd = handle_to_fd(handle); 756b66f2d16SKris Kennaway if (fd >= 0) { 757b66f2d16SKris Kennaway if (lseek(fd, off, SEEK_SET) < 0) { 758b66f2d16SKris Kennaway error("process_read: seek failed"); 759b66f2d16SKris Kennaway status = errno_to_portable(errno); 760b66f2d16SKris Kennaway } else { 761b66f2d16SKris Kennaway ret = read(fd, buf, len); 762b66f2d16SKris Kennaway if (ret < 0) { 763b66f2d16SKris Kennaway status = errno_to_portable(errno); 764b66f2d16SKris Kennaway } else if (ret == 0) { 7651e8db6e2SBrian Feldman status = SSH2_FX_EOF; 766b66f2d16SKris Kennaway } else { 767b66f2d16SKris Kennaway send_data(id, buf, ret); 7681e8db6e2SBrian Feldman status = SSH2_FX_OK; 769761efaa7SDag-Erling Smørgrav handle_update_read(handle, ret); 770b66f2d16SKris Kennaway } 771b66f2d16SKris Kennaway } 772b66f2d16SKris Kennaway } 7731e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 774b66f2d16SKris Kennaway send_status(id, status); 775b66f2d16SKris Kennaway } 776b66f2d16SKris Kennaway 777ae1f160dSDag-Erling Smørgrav static void 778f7167e0eSDag-Erling Smørgrav process_write(u_int32_t id) 779b66f2d16SKris Kennaway { 780b66f2d16SKris Kennaway u_int64_t off; 781bc5531deSDag-Erling Smørgrav size_t len; 782bc5531deSDag-Erling Smørgrav int r, handle, fd, ret, status; 783bc5531deSDag-Erling Smørgrav u_char *data; 784b66f2d16SKris Kennaway 785bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0 || 786bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u64(iqueue, &off)) != 0 || 787bc5531deSDag-Erling Smørgrav (r = sshbuf_get_string(iqueue, &data, &len)) != 0) 788bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 789b66f2d16SKris Kennaway 790bc5531deSDag-Erling Smørgrav debug("request %u: write \"%s\" (handle %d) off %llu len %zu", 791761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len); 792b66f2d16SKris Kennaway fd = handle_to_fd(handle); 793b15c8340SDag-Erling Smørgrav 794b15c8340SDag-Erling Smørgrav if (fd < 0) 795b15c8340SDag-Erling Smørgrav status = SSH2_FX_FAILURE; 796b15c8340SDag-Erling Smørgrav else { 797f7167e0eSDag-Erling Smørgrav if (!(handle_to_flags(handle) & O_APPEND) && 798f7167e0eSDag-Erling Smørgrav lseek(fd, off, SEEK_SET) < 0) { 799b66f2d16SKris Kennaway status = errno_to_portable(errno); 800b66f2d16SKris Kennaway error("process_write: seek failed"); 801b66f2d16SKris Kennaway } else { 802b66f2d16SKris Kennaway /* XXX ATOMICIO ? */ 803b66f2d16SKris Kennaway ret = write(fd, data, len); 804043840dfSDag-Erling Smørgrav if (ret < 0) { 805b66f2d16SKris Kennaway error("process_write: write failed"); 806b66f2d16SKris Kennaway status = errno_to_portable(errno); 807043840dfSDag-Erling Smørgrav } else if ((size_t)ret == len) { 8081e8db6e2SBrian Feldman status = SSH2_FX_OK; 809761efaa7SDag-Erling Smørgrav handle_update_write(handle, ret); 810b66f2d16SKris Kennaway } else { 811761efaa7SDag-Erling Smørgrav debug2("nothing at all written"); 812b15c8340SDag-Erling Smørgrav status = SSH2_FX_FAILURE; 813b66f2d16SKris Kennaway } 814b66f2d16SKris Kennaway } 815b66f2d16SKris Kennaway } 816b66f2d16SKris Kennaway send_status(id, status); 817e4a9863fSDag-Erling Smørgrav free(data); 818b66f2d16SKris Kennaway } 819b66f2d16SKris Kennaway 820ae1f160dSDag-Erling Smørgrav static void 821f7167e0eSDag-Erling Smørgrav process_do_stat(u_int32_t id, int do_lstat) 822b66f2d16SKris Kennaway { 8231e8db6e2SBrian Feldman Attrib a; 824b66f2d16SKris Kennaway struct stat st; 825b66f2d16SKris Kennaway char *name; 826bc5531deSDag-Erling Smørgrav int r, status = SSH2_FX_FAILURE; 827b66f2d16SKris Kennaway 828bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) 829bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 830bc5531deSDag-Erling Smørgrav 831761efaa7SDag-Erling Smørgrav debug3("request %u: %sstat", id, do_lstat ? "l" : ""); 832761efaa7SDag-Erling Smørgrav verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); 833bc5531deSDag-Erling Smørgrav r = do_lstat ? lstat(name, &st) : stat(name, &st); 834bc5531deSDag-Erling Smørgrav if (r < 0) { 835b66f2d16SKris Kennaway status = errno_to_portable(errno); 836b66f2d16SKris Kennaway } else { 8371e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 8381e8db6e2SBrian Feldman send_attrib(id, &a); 8391e8db6e2SBrian Feldman status = SSH2_FX_OK; 840b66f2d16SKris Kennaway } 8411e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 842b66f2d16SKris Kennaway send_status(id, status); 843e4a9863fSDag-Erling Smørgrav free(name); 844b66f2d16SKris Kennaway } 845b66f2d16SKris Kennaway 846ae1f160dSDag-Erling Smørgrav static void 847f7167e0eSDag-Erling Smørgrav process_stat(u_int32_t id) 848b66f2d16SKris Kennaway { 849f7167e0eSDag-Erling Smørgrav process_do_stat(id, 0); 850b66f2d16SKris Kennaway } 851b66f2d16SKris Kennaway 852ae1f160dSDag-Erling Smørgrav static void 853f7167e0eSDag-Erling Smørgrav process_lstat(u_int32_t id) 854b66f2d16SKris Kennaway { 855f7167e0eSDag-Erling Smørgrav process_do_stat(id, 1); 856b66f2d16SKris Kennaway } 857b66f2d16SKris Kennaway 858ae1f160dSDag-Erling Smørgrav static void 859f7167e0eSDag-Erling Smørgrav process_fstat(u_int32_t id) 860b66f2d16SKris Kennaway { 8611e8db6e2SBrian Feldman Attrib a; 862b66f2d16SKris Kennaway struct stat st; 863bc5531deSDag-Erling Smørgrav int fd, r, handle, status = SSH2_FX_FAILURE; 864b66f2d16SKris Kennaway 865bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 866bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 867761efaa7SDag-Erling Smørgrav debug("request %u: fstat \"%s\" (handle %u)", 868761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle); 869b66f2d16SKris Kennaway fd = handle_to_fd(handle); 870b66f2d16SKris Kennaway if (fd >= 0) { 871bc5531deSDag-Erling Smørgrav r = fstat(fd, &st); 872bc5531deSDag-Erling Smørgrav if (r < 0) { 873b66f2d16SKris Kennaway status = errno_to_portable(errno); 874b66f2d16SKris Kennaway } else { 8751e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 8761e8db6e2SBrian Feldman send_attrib(id, &a); 8771e8db6e2SBrian Feldman status = SSH2_FX_OK; 878b66f2d16SKris Kennaway } 879b66f2d16SKris Kennaway } 8801e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 881b66f2d16SKris Kennaway send_status(id, status); 882b66f2d16SKris Kennaway } 883b66f2d16SKris Kennaway 884ae1f160dSDag-Erling Smørgrav static struct timeval * 885efcad6b7SDag-Erling Smørgrav attrib_to_tv(const Attrib *a) 886b66f2d16SKris Kennaway { 887b66f2d16SKris Kennaway static struct timeval tv[2]; 8881e8db6e2SBrian Feldman 889b66f2d16SKris Kennaway tv[0].tv_sec = a->atime; 890b66f2d16SKris Kennaway tv[0].tv_usec = 0; 891b66f2d16SKris Kennaway tv[1].tv_sec = a->mtime; 892b66f2d16SKris Kennaway tv[1].tv_usec = 0; 893b66f2d16SKris Kennaway return tv; 894b66f2d16SKris Kennaway } 895b66f2d16SKris Kennaway 896ae1f160dSDag-Erling Smørgrav static void 897f7167e0eSDag-Erling Smørgrav process_setstat(u_int32_t id) 898b66f2d16SKris Kennaway { 899bc5531deSDag-Erling Smørgrav Attrib a; 900b66f2d16SKris Kennaway char *name; 901bc5531deSDag-Erling Smørgrav int r, status = SSH2_FX_OK; 902b66f2d16SKris Kennaway 903bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 904bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 905bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 906bc5531deSDag-Erling Smørgrav 907761efaa7SDag-Erling Smørgrav debug("request %u: setstat name \"%s\"", id, name); 908bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { 909d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu", 910bc5531deSDag-Erling Smørgrav name, (unsigned long long)a.size); 911bc5531deSDag-Erling Smørgrav r = truncate(name, a.size); 912bc5531deSDag-Erling Smørgrav if (r == -1) 913ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 914ae1f160dSDag-Erling Smørgrav } 915bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 916bc5531deSDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a.perm); 917bc5531deSDag-Erling Smørgrav r = chmod(name, a.perm & 07777); 918bc5531deSDag-Erling Smørgrav if (r == -1) 919b66f2d16SKris Kennaway status = errno_to_portable(errno); 920b66f2d16SKris Kennaway } 921bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 922761efaa7SDag-Erling Smørgrav char buf[64]; 923bc5531deSDag-Erling Smørgrav time_t t = a.mtime; 924761efaa7SDag-Erling Smørgrav 925761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 926761efaa7SDag-Erling Smørgrav localtime(&t)); 927761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf); 928bc5531deSDag-Erling Smørgrav r = utimes(name, attrib_to_tv(&a)); 929bc5531deSDag-Erling Smørgrav if (r == -1) 930b66f2d16SKris Kennaway status = errno_to_portable(errno); 931b66f2d16SKris Kennaway } 932bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { 933761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name, 934bc5531deSDag-Erling Smørgrav (u_long)a.uid, (u_long)a.gid); 935bc5531deSDag-Erling Smørgrav r = chown(name, a.uid, a.gid); 936bc5531deSDag-Erling Smørgrav if (r == -1) 9371e8db6e2SBrian Feldman status = errno_to_portable(errno); 9381e8db6e2SBrian Feldman } 939b66f2d16SKris Kennaway send_status(id, status); 940e4a9863fSDag-Erling Smørgrav free(name); 941b66f2d16SKris Kennaway } 942b66f2d16SKris Kennaway 943ae1f160dSDag-Erling Smørgrav static void 944f7167e0eSDag-Erling Smørgrav process_fsetstat(u_int32_t id) 945b66f2d16SKris Kennaway { 946bc5531deSDag-Erling Smørgrav Attrib a; 947bc5531deSDag-Erling Smørgrav int handle, fd, r; 9481e8db6e2SBrian Feldman int status = SSH2_FX_OK; 949b66f2d16SKris Kennaway 950bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0 || 951bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 952bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 953bc5531deSDag-Erling Smørgrav 954761efaa7SDag-Erling Smørgrav debug("request %u: fsetstat handle %d", id, handle); 955b66f2d16SKris Kennaway fd = handle_to_fd(handle); 956b15c8340SDag-Erling Smørgrav if (fd < 0) 9571e8db6e2SBrian Feldman status = SSH2_FX_FAILURE; 958b15c8340SDag-Erling Smørgrav else { 959761efaa7SDag-Erling Smørgrav char *name = handle_to_name(handle); 960761efaa7SDag-Erling Smørgrav 961bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { 962d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu", 963bc5531deSDag-Erling Smørgrav name, (unsigned long long)a.size); 964bc5531deSDag-Erling Smørgrav r = ftruncate(fd, a.size); 965bc5531deSDag-Erling Smørgrav if (r == -1) 966ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 967ae1f160dSDag-Erling Smørgrav } 968bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 969bc5531deSDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a.perm); 97083d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 971bc5531deSDag-Erling Smørgrav r = fchmod(fd, a.perm & 07777); 97283d2307dSDag-Erling Smørgrav #else 973bc5531deSDag-Erling Smørgrav r = chmod(name, a.perm & 07777); 97483d2307dSDag-Erling Smørgrav #endif 975bc5531deSDag-Erling Smørgrav if (r == -1) 976b66f2d16SKris Kennaway status = errno_to_portable(errno); 977b66f2d16SKris Kennaway } 978bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 979761efaa7SDag-Erling Smørgrav char buf[64]; 980bc5531deSDag-Erling Smørgrav time_t t = a.mtime; 981761efaa7SDag-Erling Smørgrav 982761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 983761efaa7SDag-Erling Smørgrav localtime(&t)); 984761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf); 98583d2307dSDag-Erling Smørgrav #ifdef HAVE_FUTIMES 986bc5531deSDag-Erling Smørgrav r = futimes(fd, attrib_to_tv(&a)); 98783d2307dSDag-Erling Smørgrav #else 988bc5531deSDag-Erling Smørgrav r = utimes(name, attrib_to_tv(&a)); 98983d2307dSDag-Erling Smørgrav #endif 990bc5531deSDag-Erling Smørgrav if (r == -1) 991b66f2d16SKris Kennaway status = errno_to_portable(errno); 992b66f2d16SKris Kennaway } 993bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { 994761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name, 995bc5531deSDag-Erling Smørgrav (u_long)a.uid, (u_long)a.gid); 99683d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHOWN 997bc5531deSDag-Erling Smørgrav r = fchown(fd, a.uid, a.gid); 99883d2307dSDag-Erling Smørgrav #else 999bc5531deSDag-Erling Smørgrav r = chown(name, a.uid, a.gid); 100083d2307dSDag-Erling Smørgrav #endif 1001bc5531deSDag-Erling Smørgrav if (r == -1) 10021e8db6e2SBrian Feldman status = errno_to_portable(errno); 10031e8db6e2SBrian Feldman } 1004b66f2d16SKris Kennaway } 1005b66f2d16SKris Kennaway send_status(id, status); 1006b66f2d16SKris Kennaway } 1007b66f2d16SKris Kennaway 1008ae1f160dSDag-Erling Smørgrav static void 1009f7167e0eSDag-Erling Smørgrav process_opendir(u_int32_t id) 1010b66f2d16SKris Kennaway { 1011b66f2d16SKris Kennaway DIR *dirp = NULL; 1012b66f2d16SKris Kennaway char *path; 1013bc5531deSDag-Erling Smørgrav int r, handle, status = SSH2_FX_FAILURE; 1014b66f2d16SKris Kennaway 1015bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 1016bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1017bc5531deSDag-Erling Smørgrav 1018761efaa7SDag-Erling Smørgrav debug3("request %u: opendir", id); 1019761efaa7SDag-Erling Smørgrav logit("opendir \"%s\"", path); 1020b66f2d16SKris Kennaway dirp = opendir(path); 1021b66f2d16SKris Kennaway if (dirp == NULL) { 1022b66f2d16SKris Kennaway status = errno_to_portable(errno); 1023b66f2d16SKris Kennaway } else { 1024f7167e0eSDag-Erling Smørgrav handle = handle_new(HANDLE_DIR, path, 0, 0, dirp); 1025b66f2d16SKris Kennaway if (handle < 0) { 1026b66f2d16SKris Kennaway closedir(dirp); 1027b66f2d16SKris Kennaway } else { 1028b66f2d16SKris Kennaway send_handle(id, handle); 10291e8db6e2SBrian Feldman status = SSH2_FX_OK; 1030b66f2d16SKris Kennaway } 1031b66f2d16SKris Kennaway 1032b66f2d16SKris Kennaway } 10331e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 1034b66f2d16SKris Kennaway send_status(id, status); 1035e4a9863fSDag-Erling Smørgrav free(path); 1036b66f2d16SKris Kennaway } 1037b66f2d16SKris Kennaway 1038ae1f160dSDag-Erling Smørgrav static void 1039f7167e0eSDag-Erling Smørgrav process_readdir(u_int32_t id) 1040b66f2d16SKris Kennaway { 1041b66f2d16SKris Kennaway DIR *dirp; 1042b66f2d16SKris Kennaway struct dirent *dp; 1043b66f2d16SKris Kennaway char *path; 1044bc5531deSDag-Erling Smørgrav int r, handle; 1045b66f2d16SKris Kennaway 1046bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 1047bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1048bc5531deSDag-Erling Smørgrav 1049761efaa7SDag-Erling Smørgrav debug("request %u: readdir \"%s\" (handle %d)", id, 1050761efaa7SDag-Erling Smørgrav handle_to_name(handle), handle); 1051b66f2d16SKris Kennaway dirp = handle_to_dir(handle); 1052b66f2d16SKris Kennaway path = handle_to_name(handle); 1053b66f2d16SKris Kennaway if (dirp == NULL || path == NULL) { 10541e8db6e2SBrian Feldman send_status(id, SSH2_FX_FAILURE); 1055b66f2d16SKris Kennaway } else { 1056b66f2d16SKris Kennaway struct stat st; 1057bc5531deSDag-Erling Smørgrav char pathname[PATH_MAX]; 1058b66f2d16SKris Kennaway Stat *stats; 1059b66f2d16SKris Kennaway int nstats = 10, count = 0, i; 1060ee21a45fSDag-Erling Smørgrav 1061761efaa7SDag-Erling Smørgrav stats = xcalloc(nstats, sizeof(Stat)); 1062b66f2d16SKris Kennaway while ((dp = readdir(dirp)) != NULL) { 1063b66f2d16SKris Kennaway if (count >= nstats) { 1064b66f2d16SKris Kennaway nstats *= 2; 1065557f75e5SDag-Erling Smørgrav stats = xreallocarray(stats, nstats, sizeof(Stat)); 1066b66f2d16SKris Kennaway } 1067b66f2d16SKris Kennaway /* XXX OVERFLOW ? */ 1068ae1f160dSDag-Erling Smørgrav snprintf(pathname, sizeof pathname, "%s%s%s", path, 1069ae1f160dSDag-Erling Smørgrav strcmp(path, "/") ? "/" : "", dp->d_name); 1070b66f2d16SKris Kennaway if (lstat(pathname, &st) < 0) 1071b66f2d16SKris Kennaway continue; 10721e8db6e2SBrian Feldman stat_to_attrib(&st, &(stats[count].attrib)); 1073b66f2d16SKris Kennaway stats[count].name = xstrdup(dp->d_name); 1074b15c8340SDag-Erling Smørgrav stats[count].long_name = ls_file(dp->d_name, &st, 0, 0); 1075b66f2d16SKris Kennaway count++; 1076b66f2d16SKris Kennaway /* send up to 100 entries in one message */ 10771e8db6e2SBrian Feldman /* XXX check packet size instead */ 1078b66f2d16SKris Kennaway if (count == 100) 1079b66f2d16SKris Kennaway break; 1080b66f2d16SKris Kennaway } 10811e8db6e2SBrian Feldman if (count > 0) { 1082b66f2d16SKris Kennaway send_names(id, count, stats); 1083b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 1084e4a9863fSDag-Erling Smørgrav free(stats[i].name); 1085e4a9863fSDag-Erling Smørgrav free(stats[i].long_name); 1086b66f2d16SKris Kennaway } 10871e8db6e2SBrian Feldman } else { 10881e8db6e2SBrian Feldman send_status(id, SSH2_FX_EOF); 10891e8db6e2SBrian Feldman } 1090e4a9863fSDag-Erling Smørgrav free(stats); 1091b66f2d16SKris Kennaway } 1092b66f2d16SKris Kennaway } 1093b66f2d16SKris Kennaway 1094ae1f160dSDag-Erling Smørgrav static void 1095f7167e0eSDag-Erling Smørgrav process_remove(u_int32_t id) 1096b66f2d16SKris Kennaway { 1097b66f2d16SKris Kennaway char *name; 1098bc5531deSDag-Erling Smørgrav int r, status = SSH2_FX_FAILURE; 1099b66f2d16SKris Kennaway 1100bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) 1101bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1102bc5531deSDag-Erling Smørgrav 1103761efaa7SDag-Erling Smørgrav debug3("request %u: remove", id); 1104761efaa7SDag-Erling Smørgrav logit("remove name \"%s\"", name); 1105bc5531deSDag-Erling Smørgrav r = unlink(name); 1106bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1107b66f2d16SKris Kennaway send_status(id, status); 1108e4a9863fSDag-Erling Smørgrav free(name); 1109b66f2d16SKris Kennaway } 1110b66f2d16SKris Kennaway 1111ae1f160dSDag-Erling Smørgrav static void 1112f7167e0eSDag-Erling Smørgrav process_mkdir(u_int32_t id) 1113b66f2d16SKris Kennaway { 1114bc5531deSDag-Erling Smørgrav Attrib a; 1115b66f2d16SKris Kennaway char *name; 1116bc5531deSDag-Erling Smørgrav int r, mode, status = SSH2_FX_FAILURE; 1117b66f2d16SKris Kennaway 1118bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 1119bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 1120bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1121bc5531deSDag-Erling Smørgrav 1122bc5531deSDag-Erling Smørgrav mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 1123bc5531deSDag-Erling Smørgrav a.perm & 07777 : 0777; 1124761efaa7SDag-Erling Smørgrav debug3("request %u: mkdir", id); 1125761efaa7SDag-Erling Smørgrav logit("mkdir name \"%s\" mode 0%o", name, mode); 1126bc5531deSDag-Erling Smørgrav r = mkdir(name, mode); 1127bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1128b66f2d16SKris Kennaway send_status(id, status); 1129e4a9863fSDag-Erling Smørgrav free(name); 1130b66f2d16SKris Kennaway } 1131b66f2d16SKris Kennaway 1132ae1f160dSDag-Erling Smørgrav static void 1133f7167e0eSDag-Erling Smørgrav process_rmdir(u_int32_t id) 1134b66f2d16SKris Kennaway { 1135b66f2d16SKris Kennaway char *name; 1136bc5531deSDag-Erling Smørgrav int r, status; 1137b66f2d16SKris Kennaway 1138bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) 1139bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1140bc5531deSDag-Erling Smørgrav 1141761efaa7SDag-Erling Smørgrav debug3("request %u: rmdir", id); 1142761efaa7SDag-Erling Smørgrav logit("rmdir name \"%s\"", name); 1143bc5531deSDag-Erling Smørgrav r = rmdir(name); 1144bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1145b66f2d16SKris Kennaway send_status(id, status); 1146e4a9863fSDag-Erling Smørgrav free(name); 1147b66f2d16SKris Kennaway } 1148b66f2d16SKris Kennaway 1149ae1f160dSDag-Erling Smørgrav static void 1150f7167e0eSDag-Erling Smørgrav process_realpath(u_int32_t id) 1151b66f2d16SKris Kennaway { 1152bc5531deSDag-Erling Smørgrav char resolvedname[PATH_MAX]; 1153b66f2d16SKris Kennaway char *path; 1154bc5531deSDag-Erling Smørgrav int r; 1155b66f2d16SKris Kennaway 1156bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 1157bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1158bc5531deSDag-Erling Smørgrav 11591e8db6e2SBrian Feldman if (path[0] == '\0') { 1160e4a9863fSDag-Erling Smørgrav free(path); 11611e8db6e2SBrian Feldman path = xstrdup("."); 11621e8db6e2SBrian Feldman } 1163761efaa7SDag-Erling Smørgrav debug3("request %u: realpath", id); 1164761efaa7SDag-Erling Smørgrav verbose("realpath \"%s\"", path); 1165b66f2d16SKris Kennaway if (realpath(path, resolvedname) == NULL) { 1166b66f2d16SKris Kennaway send_status(id, errno_to_portable(errno)); 1167b66f2d16SKris Kennaway } else { 1168b66f2d16SKris Kennaway Stat s; 1169b66f2d16SKris Kennaway attrib_clear(&s.attrib); 1170b66f2d16SKris Kennaway s.name = s.long_name = resolvedname; 1171b66f2d16SKris Kennaway send_names(id, 1, &s); 1172b66f2d16SKris Kennaway } 1173e4a9863fSDag-Erling Smørgrav free(path); 1174b66f2d16SKris Kennaway } 1175b66f2d16SKris Kennaway 1176ae1f160dSDag-Erling Smørgrav static void 1177f7167e0eSDag-Erling Smørgrav process_rename(u_int32_t id) 1178b66f2d16SKris Kennaway { 1179b66f2d16SKris Kennaway char *oldpath, *newpath; 1180bc5531deSDag-Erling Smørgrav int r, status; 1181d0c8c0bcSDag-Erling Smørgrav struct stat sb; 1182b66f2d16SKris Kennaway 1183bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1184bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 1185bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1186bc5531deSDag-Erling Smørgrav 1187761efaa7SDag-Erling Smørgrav debug3("request %u: rename", id); 1188761efaa7SDag-Erling Smørgrav logit("rename old \"%s\" new \"%s\"", oldpath, newpath); 1189d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_FAILURE; 1190f7167e0eSDag-Erling Smørgrav if (lstat(oldpath, &sb) == -1) 1191d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1192d0c8c0bcSDag-Erling Smørgrav else if (S_ISREG(sb.st_mode)) { 1193d0c8c0bcSDag-Erling Smørgrav /* Race-free rename of regular files */ 1194d74d50a8SDag-Erling Smørgrav if (link(oldpath, newpath) == -1) { 11957aee6ffeSDag-Erling Smørgrav if (errno == EOPNOTSUPP || errno == ENOSYS 1196d4af9e69SDag-Erling Smørgrav #ifdef EXDEV 1197d4af9e69SDag-Erling Smørgrav || errno == EXDEV 1198d4af9e69SDag-Erling Smørgrav #endif 1199d74d50a8SDag-Erling Smørgrav #ifdef LINK_OPNOTSUPP_ERRNO 1200d74d50a8SDag-Erling Smørgrav || errno == LINK_OPNOTSUPP_ERRNO 1201d74d50a8SDag-Erling Smørgrav #endif 1202d74d50a8SDag-Erling Smørgrav ) { 1203d74d50a8SDag-Erling Smørgrav struct stat st; 1204d74d50a8SDag-Erling Smørgrav 1205d74d50a8SDag-Erling Smørgrav /* 1206d74d50a8SDag-Erling Smørgrav * fs doesn't support links, so fall back to 1207d74d50a8SDag-Erling Smørgrav * stat+rename. This is racy. 1208d74d50a8SDag-Erling Smørgrav */ 1209d74d50a8SDag-Erling Smørgrav if (stat(newpath, &st) == -1) { 1210d74d50a8SDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1211d74d50a8SDag-Erling Smørgrav status = 1212d74d50a8SDag-Erling Smørgrav errno_to_portable(errno); 1213d74d50a8SDag-Erling Smørgrav else 1214d74d50a8SDag-Erling Smørgrav status = SSH2_FX_OK; 1215d74d50a8SDag-Erling Smørgrav } 1216d74d50a8SDag-Erling Smørgrav } else { 1217d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1218d74d50a8SDag-Erling Smørgrav } 1219d74d50a8SDag-Erling Smørgrav } else if (unlink(oldpath) == -1) { 1220d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1221d0c8c0bcSDag-Erling Smørgrav /* clean spare link */ 1222d0c8c0bcSDag-Erling Smørgrav unlink(newpath); 1223d0c8c0bcSDag-Erling Smørgrav } else 1224d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 1225d0c8c0bcSDag-Erling Smørgrav } else if (stat(newpath, &sb) == -1) { 1226d0c8c0bcSDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1227d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1228d0c8c0bcSDag-Erling Smørgrav else 1229d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 12301e8db6e2SBrian Feldman } 1231b66f2d16SKris Kennaway send_status(id, status); 1232e4a9863fSDag-Erling Smørgrav free(oldpath); 1233e4a9863fSDag-Erling Smørgrav free(newpath); 1234b66f2d16SKris Kennaway } 1235b66f2d16SKris Kennaway 1236ae1f160dSDag-Erling Smørgrav static void 1237f7167e0eSDag-Erling Smørgrav process_readlink(u_int32_t id) 12381e8db6e2SBrian Feldman { 1239bc5531deSDag-Erling Smørgrav int r, len; 1240bc5531deSDag-Erling Smørgrav char buf[PATH_MAX]; 12411e8db6e2SBrian Feldman char *path; 12421e8db6e2SBrian Feldman 1243bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 1244bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1245bc5531deSDag-Erling Smørgrav 1246761efaa7SDag-Erling Smørgrav debug3("request %u: readlink", id); 1247761efaa7SDag-Erling Smørgrav verbose("readlink \"%s\"", path); 1248d74d50a8SDag-Erling Smørgrav if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) 12491e8db6e2SBrian Feldman send_status(id, errno_to_portable(errno)); 12501e8db6e2SBrian Feldman else { 12511e8db6e2SBrian Feldman Stat s; 12521e8db6e2SBrian Feldman 1253d74d50a8SDag-Erling Smørgrav buf[len] = '\0'; 12541e8db6e2SBrian Feldman attrib_clear(&s.attrib); 1255d74d50a8SDag-Erling Smørgrav s.name = s.long_name = buf; 12561e8db6e2SBrian Feldman send_names(id, 1, &s); 12571e8db6e2SBrian Feldman } 1258e4a9863fSDag-Erling Smørgrav free(path); 12591e8db6e2SBrian Feldman } 12601e8db6e2SBrian Feldman 1261ae1f160dSDag-Erling Smørgrav static void 1262f7167e0eSDag-Erling Smørgrav process_symlink(u_int32_t id) 12631e8db6e2SBrian Feldman { 12641e8db6e2SBrian Feldman char *oldpath, *newpath; 1265bc5531deSDag-Erling Smørgrav int r, status; 12661e8db6e2SBrian Feldman 1267bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1268bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 1269bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1270bc5531deSDag-Erling Smørgrav 1271761efaa7SDag-Erling Smørgrav debug3("request %u: symlink", id); 1272761efaa7SDag-Erling Smørgrav logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); 1273d0c8c0bcSDag-Erling Smørgrav /* this will fail if 'newpath' exists */ 1274bc5531deSDag-Erling Smørgrav r = symlink(oldpath, newpath); 1275bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 12761e8db6e2SBrian Feldman send_status(id, status); 1277e4a9863fSDag-Erling Smørgrav free(oldpath); 1278e4a9863fSDag-Erling Smørgrav free(newpath); 12791e8db6e2SBrian Feldman } 12801e8db6e2SBrian Feldman 1281ae1f160dSDag-Erling Smørgrav static void 1282d4af9e69SDag-Erling Smørgrav process_extended_posix_rename(u_int32_t id) 1283d4af9e69SDag-Erling Smørgrav { 1284d4af9e69SDag-Erling Smørgrav char *oldpath, *newpath; 1285bc5531deSDag-Erling Smørgrav int r, status; 1286d4af9e69SDag-Erling Smørgrav 1287bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1288bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 1289bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1290bc5531deSDag-Erling Smørgrav 1291d4af9e69SDag-Erling Smørgrav debug3("request %u: posix-rename", id); 1292d4af9e69SDag-Erling Smørgrav logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); 1293bc5531deSDag-Erling Smørgrav r = rename(oldpath, newpath); 1294bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1295b15c8340SDag-Erling Smørgrav send_status(id, status); 1296e4a9863fSDag-Erling Smørgrav free(oldpath); 1297e4a9863fSDag-Erling Smørgrav free(newpath); 1298d4af9e69SDag-Erling Smørgrav } 1299d4af9e69SDag-Erling Smørgrav 1300d4af9e69SDag-Erling Smørgrav static void 1301d4af9e69SDag-Erling Smørgrav process_extended_statvfs(u_int32_t id) 1302d4af9e69SDag-Erling Smørgrav { 1303d4af9e69SDag-Erling Smørgrav char *path; 1304d4af9e69SDag-Erling Smørgrav struct statvfs st; 1305bc5531deSDag-Erling Smørgrav int r; 1306d4af9e69SDag-Erling Smørgrav 1307bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 1308bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1309f7167e0eSDag-Erling Smørgrav debug3("request %u: statvfs", id); 1310f7167e0eSDag-Erling Smørgrav logit("statvfs \"%s\"", path); 1311d4af9e69SDag-Erling Smørgrav 1312d4af9e69SDag-Erling Smørgrav if (statvfs(path, &st) != 0) 1313d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1314d4af9e69SDag-Erling Smørgrav else 1315d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st); 1316e4a9863fSDag-Erling Smørgrav free(path); 1317d4af9e69SDag-Erling Smørgrav } 1318d4af9e69SDag-Erling Smørgrav 1319d4af9e69SDag-Erling Smørgrav static void 1320d4af9e69SDag-Erling Smørgrav process_extended_fstatvfs(u_int32_t id) 1321d4af9e69SDag-Erling Smørgrav { 1322bc5531deSDag-Erling Smørgrav int r, handle, fd; 1323d4af9e69SDag-Erling Smørgrav struct statvfs st; 1324d4af9e69SDag-Erling Smørgrav 1325bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 1326bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1327d4af9e69SDag-Erling Smørgrav debug("request %u: fstatvfs \"%s\" (handle %u)", 1328d4af9e69SDag-Erling Smørgrav id, handle_to_name(handle), handle); 1329d4af9e69SDag-Erling Smørgrav if ((fd = handle_to_fd(handle)) < 0) { 1330d4af9e69SDag-Erling Smørgrav send_status(id, SSH2_FX_FAILURE); 1331d4af9e69SDag-Erling Smørgrav return; 1332d4af9e69SDag-Erling Smørgrav } 1333d4af9e69SDag-Erling Smørgrav if (fstatvfs(fd, &st) != 0) 1334d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1335d4af9e69SDag-Erling Smørgrav else 1336d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st); 1337d4af9e69SDag-Erling Smørgrav } 1338d4af9e69SDag-Erling Smørgrav 1339d4af9e69SDag-Erling Smørgrav static void 13404a421b63SDag-Erling Smørgrav process_extended_hardlink(u_int32_t id) 13414a421b63SDag-Erling Smørgrav { 13424a421b63SDag-Erling Smørgrav char *oldpath, *newpath; 1343bc5531deSDag-Erling Smørgrav int r, status; 13444a421b63SDag-Erling Smørgrav 1345bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1346bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 1347bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1348bc5531deSDag-Erling Smørgrav 13494a421b63SDag-Erling Smørgrav debug3("request %u: hardlink", id); 13504a421b63SDag-Erling Smørgrav logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath); 1351bc5531deSDag-Erling Smørgrav r = link(oldpath, newpath); 1352bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 13534a421b63SDag-Erling Smørgrav send_status(id, status); 1354e4a9863fSDag-Erling Smørgrav free(oldpath); 1355e4a9863fSDag-Erling Smørgrav free(newpath); 13564a421b63SDag-Erling Smørgrav } 13574a421b63SDag-Erling Smørgrav 13584a421b63SDag-Erling Smørgrav static void 1359f7167e0eSDag-Erling Smørgrav process_extended_fsync(u_int32_t id) 13601e8db6e2SBrian Feldman { 1361bc5531deSDag-Erling Smørgrav int handle, fd, r, status = SSH2_FX_OP_UNSUPPORTED; 13621e8db6e2SBrian Feldman 1363bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 1364bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1365f7167e0eSDag-Erling Smørgrav debug3("request %u: fsync (handle %u)", id, handle); 1366f7167e0eSDag-Erling Smørgrav verbose("fsync \"%s\"", handle_to_name(handle)); 1367f7167e0eSDag-Erling Smørgrav if ((fd = handle_to_fd(handle)) < 0) 1368f7167e0eSDag-Erling Smørgrav status = SSH2_FX_NO_SUCH_FILE; 1369f7167e0eSDag-Erling Smørgrav else if (handle_is_ok(handle, HANDLE_FILE)) { 1370bc5531deSDag-Erling Smørgrav r = fsync(fd); 1371bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1372f7167e0eSDag-Erling Smørgrav } 1373f7167e0eSDag-Erling Smørgrav send_status(id, status); 1374f7167e0eSDag-Erling Smørgrav } 1375f7167e0eSDag-Erling Smørgrav 1376f7167e0eSDag-Erling Smørgrav static void 1377f7167e0eSDag-Erling Smørgrav process_extended(u_int32_t id) 1378f7167e0eSDag-Erling Smørgrav { 1379f7167e0eSDag-Erling Smørgrav char *request; 1380bc5531deSDag-Erling Smørgrav int i, r; 1381f7167e0eSDag-Erling Smørgrav 1382bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &request, NULL)) != 0) 1383bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1384f7167e0eSDag-Erling Smørgrav for (i = 0; extended_handlers[i].handler != NULL; i++) { 1385f7167e0eSDag-Erling Smørgrav if (strcmp(request, extended_handlers[i].ext_name) == 0) { 1386f7167e0eSDag-Erling Smørgrav if (!request_permitted(&extended_handlers[i])) 1387f7167e0eSDag-Erling Smørgrav send_status(id, SSH2_FX_PERMISSION_DENIED); 1388d4af9e69SDag-Erling Smørgrav else 1389f7167e0eSDag-Erling Smørgrav extended_handlers[i].handler(id); 1390f7167e0eSDag-Erling Smørgrav break; 1391f7167e0eSDag-Erling Smørgrav } 1392f7167e0eSDag-Erling Smørgrav } 1393f7167e0eSDag-Erling Smørgrav if (extended_handlers[i].handler == NULL) { 1394f7167e0eSDag-Erling Smørgrav error("Unknown extended request \"%.100s\"", request); 13951e8db6e2SBrian Feldman send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 1396f7167e0eSDag-Erling Smørgrav } 1397e4a9863fSDag-Erling Smørgrav free(request); 13981e8db6e2SBrian Feldman } 1399b66f2d16SKris Kennaway 1400b66f2d16SKris Kennaway /* stolen from ssh-agent */ 1401b66f2d16SKris Kennaway 1402ae1f160dSDag-Erling Smørgrav static void 1403b66f2d16SKris Kennaway process(void) 1404b66f2d16SKris Kennaway { 1405bc5531deSDag-Erling Smørgrav u_int msg_len; 1406bc5531deSDag-Erling Smørgrav u_int buf_len; 1407bc5531deSDag-Erling Smørgrav u_int consumed; 1408bc5531deSDag-Erling Smørgrav u_char type; 1409bc5531deSDag-Erling Smørgrav const u_char *cp; 1410bc5531deSDag-Erling Smørgrav int i, r; 1411f7167e0eSDag-Erling Smørgrav u_int32_t id; 1412b66f2d16SKris Kennaway 1413bc5531deSDag-Erling Smørgrav buf_len = sshbuf_len(iqueue); 1414545d5ecaSDag-Erling Smørgrav if (buf_len < 5) 1415b66f2d16SKris Kennaway return; /* Incomplete message. */ 1416bc5531deSDag-Erling Smørgrav cp = sshbuf_ptr(iqueue); 1417761efaa7SDag-Erling Smørgrav msg_len = get_u32(cp); 1418021d409fSDag-Erling Smørgrav if (msg_len > SFTP_MAX_MSG_LENGTH) { 1419761efaa7SDag-Erling Smørgrav error("bad message from %s local user %s", 1420761efaa7SDag-Erling Smørgrav client_addr, pw->pw_name); 1421d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(11); 1422b66f2d16SKris Kennaway } 1423545d5ecaSDag-Erling Smørgrav if (buf_len < msg_len + 4) 1424b66f2d16SKris Kennaway return; 1425bc5531deSDag-Erling Smørgrav if ((r = sshbuf_consume(iqueue, 4)) != 0) 1426bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1427545d5ecaSDag-Erling Smørgrav buf_len -= 4; 1428bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u8(iqueue, &type)) != 0) 1429bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1430f7167e0eSDag-Erling Smørgrav 1431b66f2d16SKris Kennaway switch (type) { 14321e8db6e2SBrian Feldman case SSH2_FXP_INIT: 1433b66f2d16SKris Kennaway process_init(); 1434f7167e0eSDag-Erling Smørgrav init_done = 1; 14351e8db6e2SBrian Feldman break; 14361e8db6e2SBrian Feldman case SSH2_FXP_EXTENDED: 1437f7167e0eSDag-Erling Smørgrav if (!init_done) 1438f7167e0eSDag-Erling Smørgrav fatal("Received extended request before init"); 1439bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(iqueue, &id)) != 0) 1440bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1441f7167e0eSDag-Erling Smørgrav process_extended(id); 14421e8db6e2SBrian Feldman break; 1443b66f2d16SKris Kennaway default: 1444f7167e0eSDag-Erling Smørgrav if (!init_done) 1445f7167e0eSDag-Erling Smørgrav fatal("Received %u request before init", type); 1446bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(iqueue, &id)) != 0) 1447bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1448f7167e0eSDag-Erling Smørgrav for (i = 0; handlers[i].handler != NULL; i++) { 1449f7167e0eSDag-Erling Smørgrav if (type == handlers[i].type) { 1450f7167e0eSDag-Erling Smørgrav if (!request_permitted(&handlers[i])) { 1451f7167e0eSDag-Erling Smørgrav send_status(id, 1452f7167e0eSDag-Erling Smørgrav SSH2_FX_PERMISSION_DENIED); 1453f7167e0eSDag-Erling Smørgrav } else { 1454f7167e0eSDag-Erling Smørgrav handlers[i].handler(id); 1455f7167e0eSDag-Erling Smørgrav } 1456b66f2d16SKris Kennaway break; 1457b66f2d16SKris Kennaway } 1458f7167e0eSDag-Erling Smørgrav } 1459f7167e0eSDag-Erling Smørgrav if (handlers[i].handler == NULL) 1460f7167e0eSDag-Erling Smørgrav error("Unknown message %u", type); 1461f7167e0eSDag-Erling Smørgrav } 1462545d5ecaSDag-Erling Smørgrav /* discard the remaining bytes from the current packet */ 1463bc5531deSDag-Erling Smørgrav if (buf_len < sshbuf_len(iqueue)) { 1464d4af9e69SDag-Erling Smørgrav error("iqueue grew unexpectedly"); 1465d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1466d4af9e69SDag-Erling Smørgrav } 1467bc5531deSDag-Erling Smørgrav consumed = buf_len - sshbuf_len(iqueue); 1468d4af9e69SDag-Erling Smørgrav if (msg_len < consumed) { 1469f7167e0eSDag-Erling Smørgrav error("msg_len %u < consumed %u", msg_len, consumed); 1470d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1471d4af9e69SDag-Erling Smørgrav } 1472bc5531deSDag-Erling Smørgrav if (msg_len > consumed && 1473bc5531deSDag-Erling Smørgrav (r = sshbuf_consume(iqueue, msg_len - consumed)) != 0) 1474bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1475b66f2d16SKris Kennaway } 1476b66f2d16SKris Kennaway 1477761efaa7SDag-Erling Smørgrav /* Cleanup handler that logs active handles upon normal exit */ 1478761efaa7SDag-Erling Smørgrav void 1479d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(int i) 1480761efaa7SDag-Erling Smørgrav { 1481761efaa7SDag-Erling Smørgrav if (pw != NULL && client_addr != NULL) { 1482761efaa7SDag-Erling Smørgrav handle_log_exit(); 1483761efaa7SDag-Erling Smørgrav logit("session closed for local user %s from [%s]", 1484761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr); 1485761efaa7SDag-Erling Smørgrav } 1486761efaa7SDag-Erling Smørgrav _exit(i); 1487761efaa7SDag-Erling Smørgrav } 1488761efaa7SDag-Erling Smørgrav 1489761efaa7SDag-Erling Smørgrav static void 1490d4af9e69SDag-Erling Smørgrav sftp_server_usage(void) 1491761efaa7SDag-Erling Smørgrav { 1492761efaa7SDag-Erling Smørgrav extern char *__progname; 1493761efaa7SDag-Erling Smørgrav 1494761efaa7SDag-Erling Smørgrav fprintf(stderr, 14956888a9beSDag-Erling Smørgrav "usage: %s [-ehR] [-d start_directory] [-f log_facility] " 1496f7167e0eSDag-Erling Smørgrav "[-l log_level]\n\t[-P blacklisted_requests] " 1497f7167e0eSDag-Erling Smørgrav "[-p whitelisted_requests] [-u umask]\n" 1498f7167e0eSDag-Erling Smørgrav " %s -Q protocol_feature\n", 1499f7167e0eSDag-Erling Smørgrav __progname, __progname); 1500761efaa7SDag-Erling Smørgrav exit(1); 1501761efaa7SDag-Erling Smørgrav } 1502761efaa7SDag-Erling Smørgrav 1503b66f2d16SKris Kennaway int 1504d4af9e69SDag-Erling Smørgrav sftp_server_main(int argc, char **argv, struct passwd *user_pw) 1505b66f2d16SKris Kennaway { 15061e8db6e2SBrian Feldman fd_set *rset, *wset; 1507bc5531deSDag-Erling Smørgrav int i, r, in, out, max, ch, skipargs = 0, log_stderr = 0; 15081e8db6e2SBrian Feldman ssize_t len, olen, set_size; 1509761efaa7SDag-Erling Smørgrav SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; 15106888a9beSDag-Erling Smørgrav char *cp, *homedir = NULL, buf[4*4096]; 15114a421b63SDag-Erling Smørgrav long mask; 1512761efaa7SDag-Erling Smørgrav 1513761efaa7SDag-Erling Smørgrav extern char *optarg; 1514761efaa7SDag-Erling Smørgrav extern char *__progname; 15151e8db6e2SBrian Feldman 1516*acc1a9efSDag-Erling Smørgrav ssh_malloc_init(); /* must be called before any mallocs */ 1517761efaa7SDag-Erling Smørgrav __progname = ssh_get_progname(argv[0]); 1518761efaa7SDag-Erling Smørgrav log_init(__progname, log_level, log_facility, log_stderr); 1519b66f2d16SKris Kennaway 15206888a9beSDag-Erling Smørgrav pw = pwcopy(user_pw); 15216888a9beSDag-Erling Smørgrav 1522f7167e0eSDag-Erling Smørgrav while (!skipargs && (ch = getopt(argc, argv, 1523f7167e0eSDag-Erling Smørgrav "d:f:l:P:p:Q:u:cehR")) != -1) { 1524761efaa7SDag-Erling Smørgrav switch (ch) { 1525f7167e0eSDag-Erling Smørgrav case 'Q': 1526f7167e0eSDag-Erling Smørgrav if (strcasecmp(optarg, "requests") != 0) { 1527f7167e0eSDag-Erling Smørgrav fprintf(stderr, "Invalid query type\n"); 1528f7167e0eSDag-Erling Smørgrav exit(1); 1529f7167e0eSDag-Erling Smørgrav } 1530f7167e0eSDag-Erling Smørgrav for (i = 0; handlers[i].handler != NULL; i++) 1531f7167e0eSDag-Erling Smørgrav printf("%s\n", handlers[i].name); 1532f7167e0eSDag-Erling Smørgrav for (i = 0; extended_handlers[i].handler != NULL; i++) 1533f7167e0eSDag-Erling Smørgrav printf("%s\n", extended_handlers[i].name); 1534f7167e0eSDag-Erling Smørgrav exit(0); 1535f7167e0eSDag-Erling Smørgrav break; 1536b15c8340SDag-Erling Smørgrav case 'R': 1537b15c8340SDag-Erling Smørgrav readonly = 1; 1538b15c8340SDag-Erling Smørgrav break; 1539761efaa7SDag-Erling Smørgrav case 'c': 1540761efaa7SDag-Erling Smørgrav /* 1541761efaa7SDag-Erling Smørgrav * Ignore all arguments if we are invoked as a 1542761efaa7SDag-Erling Smørgrav * shell using "sftp-server -c command" 1543761efaa7SDag-Erling Smørgrav */ 1544761efaa7SDag-Erling Smørgrav skipargs = 1; 1545761efaa7SDag-Erling Smørgrav break; 1546761efaa7SDag-Erling Smørgrav case 'e': 1547761efaa7SDag-Erling Smørgrav log_stderr = 1; 1548761efaa7SDag-Erling Smørgrav break; 1549761efaa7SDag-Erling Smørgrav case 'l': 1550761efaa7SDag-Erling Smørgrav log_level = log_level_number(optarg); 1551761efaa7SDag-Erling Smørgrav if (log_level == SYSLOG_LEVEL_NOT_SET) 1552761efaa7SDag-Erling Smørgrav error("Invalid log level \"%s\"", optarg); 1553761efaa7SDag-Erling Smørgrav break; 1554761efaa7SDag-Erling Smørgrav case 'f': 1555761efaa7SDag-Erling Smørgrav log_facility = log_facility_number(optarg); 1556d4af9e69SDag-Erling Smørgrav if (log_facility == SYSLOG_FACILITY_NOT_SET) 1557761efaa7SDag-Erling Smørgrav error("Invalid log facility \"%s\"", optarg); 1558761efaa7SDag-Erling Smørgrav break; 15596888a9beSDag-Erling Smørgrav case 'd': 15606888a9beSDag-Erling Smørgrav cp = tilde_expand_filename(optarg, user_pw->pw_uid); 15616888a9beSDag-Erling Smørgrav homedir = percent_expand(cp, "d", user_pw->pw_dir, 15626888a9beSDag-Erling Smørgrav "u", user_pw->pw_name, (char *)NULL); 15636888a9beSDag-Erling Smørgrav free(cp); 15646888a9beSDag-Erling Smørgrav break; 1565f7167e0eSDag-Erling Smørgrav case 'p': 1566f7167e0eSDag-Erling Smørgrav if (request_whitelist != NULL) 1567f7167e0eSDag-Erling Smørgrav fatal("Permitted requests already set"); 1568f7167e0eSDag-Erling Smørgrav request_whitelist = xstrdup(optarg); 1569f7167e0eSDag-Erling Smørgrav break; 1570f7167e0eSDag-Erling Smørgrav case 'P': 1571f7167e0eSDag-Erling Smørgrav if (request_blacklist != NULL) 1572f7167e0eSDag-Erling Smørgrav fatal("Refused requests already set"); 1573f7167e0eSDag-Erling Smørgrav request_blacklist = xstrdup(optarg); 1574f7167e0eSDag-Erling Smørgrav break; 1575b15c8340SDag-Erling Smørgrav case 'u': 15764a421b63SDag-Erling Smørgrav errno = 0; 15774a421b63SDag-Erling Smørgrav mask = strtol(optarg, &cp, 8); 15784a421b63SDag-Erling Smørgrav if (mask < 0 || mask > 0777 || *cp != '\0' || 15794a421b63SDag-Erling Smørgrav cp == optarg || (mask == 0 && errno != 0)) 15804a421b63SDag-Erling Smørgrav fatal("Invalid umask \"%s\"", optarg); 15814a421b63SDag-Erling Smørgrav (void)umask((mode_t)mask); 1582b15c8340SDag-Erling Smørgrav break; 1583761efaa7SDag-Erling Smørgrav case 'h': 1584761efaa7SDag-Erling Smørgrav default: 1585d4af9e69SDag-Erling Smørgrav sftp_server_usage(); 1586761efaa7SDag-Erling Smørgrav } 1587761efaa7SDag-Erling Smørgrav } 1588761efaa7SDag-Erling Smørgrav 1589761efaa7SDag-Erling Smørgrav log_init(__progname, log_level, log_facility, log_stderr); 1590761efaa7SDag-Erling Smørgrav 1591a0ee8cc6SDag-Erling Smørgrav #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) 1592a0ee8cc6SDag-Erling Smørgrav /* 1593a0ee8cc6SDag-Erling Smørgrav * On Linux, we should try to avoid making /proc/self/{mem,maps} 1594a0ee8cc6SDag-Erling Smørgrav * available to the user so that sftp access doesn't automatically 1595a0ee8cc6SDag-Erling Smørgrav * imply arbitrary code execution access that will break 1596a0ee8cc6SDag-Erling Smørgrav * restricted configurations. 1597a0ee8cc6SDag-Erling Smørgrav */ 1598a0ee8cc6SDag-Erling Smørgrav if (prctl(PR_SET_DUMPABLE, 0) != 0) 1599a0ee8cc6SDag-Erling Smørgrav fatal("unable to make the process undumpable"); 1600a0ee8cc6SDag-Erling Smørgrav #endif /* defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) */ 1601a0ee8cc6SDag-Erling Smørgrav 1602*acc1a9efSDag-Erling Smørgrav /* Drop any fine-grained privileges we don't need */ 1603*acc1a9efSDag-Erling Smørgrav platform_pledge_sftp_server(); 1604*acc1a9efSDag-Erling Smørgrav 1605761efaa7SDag-Erling Smørgrav if ((cp = getenv("SSH_CONNECTION")) != NULL) { 1606761efaa7SDag-Erling Smørgrav client_addr = xstrdup(cp); 1607d4af9e69SDag-Erling Smørgrav if ((cp = strchr(client_addr, ' ')) == NULL) { 1608d4af9e69SDag-Erling Smørgrav error("Malformed SSH_CONNECTION variable: \"%s\"", 1609761efaa7SDag-Erling Smørgrav getenv("SSH_CONNECTION")); 1610d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1611d4af9e69SDag-Erling Smørgrav } 1612761efaa7SDag-Erling Smørgrav *cp = '\0'; 1613761efaa7SDag-Erling Smørgrav } else 1614761efaa7SDag-Erling Smørgrav client_addr = xstrdup("UNKNOWN"); 1615761efaa7SDag-Erling Smørgrav 1616761efaa7SDag-Erling Smørgrav logit("session opened for local user %s from [%s]", 1617761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr); 1618761efaa7SDag-Erling Smørgrav 1619b15c8340SDag-Erling Smørgrav in = STDIN_FILENO; 1620b15c8340SDag-Erling Smørgrav out = STDOUT_FILENO; 1621b66f2d16SKris Kennaway 162283d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN 162383d2307dSDag-Erling Smørgrav setmode(in, O_BINARY); 162483d2307dSDag-Erling Smørgrav setmode(out, O_BINARY); 162583d2307dSDag-Erling Smørgrav #endif 162683d2307dSDag-Erling Smørgrav 1627b66f2d16SKris Kennaway max = 0; 1628b66f2d16SKris Kennaway if (in > max) 1629b66f2d16SKris Kennaway max = in; 1630b66f2d16SKris Kennaway if (out > max) 1631b66f2d16SKris Kennaway max = out; 1632b66f2d16SKris Kennaway 1633bc5531deSDag-Erling Smørgrav if ((iqueue = sshbuf_new()) == NULL) 1634bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 1635bc5531deSDag-Erling Smørgrav if ((oqueue = sshbuf_new()) == NULL) 1636bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 1637b66f2d16SKris Kennaway 1638*acc1a9efSDag-Erling Smørgrav rset = xcalloc(howmany(max + 1, NFDBITS), sizeof(fd_mask)); 1639*acc1a9efSDag-Erling Smørgrav wset = xcalloc(howmany(max + 1, NFDBITS), sizeof(fd_mask)); 1640b66f2d16SKris Kennaway 16416888a9beSDag-Erling Smørgrav if (homedir != NULL) { 16426888a9beSDag-Erling Smørgrav if (chdir(homedir) != 0) { 16436888a9beSDag-Erling Smørgrav error("chdir to \"%s\" failed: %s", homedir, 16446888a9beSDag-Erling Smørgrav strerror(errno)); 16456888a9beSDag-Erling Smørgrav } 16466888a9beSDag-Erling Smørgrav } 16476888a9beSDag-Erling Smørgrav 1648*acc1a9efSDag-Erling Smørgrav set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 16491e8db6e2SBrian Feldman for (;;) { 16501e8db6e2SBrian Feldman memset(rset, 0, set_size); 16511e8db6e2SBrian Feldman memset(wset, 0, set_size); 16521e8db6e2SBrian Feldman 1653d4af9e69SDag-Erling Smørgrav /* 1654d4af9e69SDag-Erling Smørgrav * Ensure that we can read a full buffer and handle 1655d4af9e69SDag-Erling Smørgrav * the worst-case length packet it can generate, 1656d4af9e69SDag-Erling Smørgrav * otherwise apply backpressure by stopping reads. 1657d4af9e69SDag-Erling Smørgrav */ 1658bc5531deSDag-Erling Smørgrav if ((r = sshbuf_check_reserve(iqueue, sizeof(buf))) == 0 && 1659bc5531deSDag-Erling Smørgrav (r = sshbuf_check_reserve(oqueue, 1660bc5531deSDag-Erling Smørgrav SFTP_MAX_MSG_LENGTH)) == 0) 16611e8db6e2SBrian Feldman FD_SET(in, rset); 1662bc5531deSDag-Erling Smørgrav else if (r != SSH_ERR_NO_BUFFER_SPACE) 1663bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_check_reserve failed: %s", 1664bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1665d4af9e69SDag-Erling Smørgrav 1666bc5531deSDag-Erling Smørgrav olen = sshbuf_len(oqueue); 1667b66f2d16SKris Kennaway if (olen > 0) 16681e8db6e2SBrian Feldman FD_SET(out, wset); 1669b66f2d16SKris Kennaway 16701e8db6e2SBrian Feldman if (select(max+1, rset, wset, NULL, NULL) < 0) { 1671b66f2d16SKris Kennaway if (errno == EINTR) 1672b66f2d16SKris Kennaway continue; 1673761efaa7SDag-Erling Smørgrav error("select: %s", strerror(errno)); 1674d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(2); 1675b66f2d16SKris Kennaway } 1676b66f2d16SKris Kennaway 1677b66f2d16SKris Kennaway /* copy stdin to iqueue */ 16781e8db6e2SBrian Feldman if (FD_ISSET(in, rset)) { 1679b66f2d16SKris Kennaway len = read(in, buf, sizeof buf); 1680b66f2d16SKris Kennaway if (len == 0) { 1681b66f2d16SKris Kennaway debug("read eof"); 1682d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(0); 1683b66f2d16SKris Kennaway } else if (len < 0) { 1684761efaa7SDag-Erling Smørgrav error("read: %s", strerror(errno)); 1685d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1); 1686bc5531deSDag-Erling Smørgrav } else if ((r = sshbuf_put(iqueue, buf, len)) != 0) { 1687bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", 1688bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1689b66f2d16SKris Kennaway } 1690b66f2d16SKris Kennaway } 1691b66f2d16SKris Kennaway /* send oqueue to stdout */ 16921e8db6e2SBrian Feldman if (FD_ISSET(out, wset)) { 1693bc5531deSDag-Erling Smørgrav len = write(out, sshbuf_ptr(oqueue), olen); 1694b66f2d16SKris Kennaway if (len < 0) { 1695761efaa7SDag-Erling Smørgrav error("write: %s", strerror(errno)); 1696d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1); 1697bc5531deSDag-Erling Smørgrav } else if ((r = sshbuf_consume(oqueue, len)) != 0) { 1698bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", 1699bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1700b66f2d16SKris Kennaway } 1701b66f2d16SKris Kennaway } 1702d4af9e69SDag-Erling Smørgrav 1703d4af9e69SDag-Erling Smørgrav /* 1704d4af9e69SDag-Erling Smørgrav * Process requests from client if we can fit the results 1705d4af9e69SDag-Erling Smørgrav * into the output buffer, otherwise stop processing input 1706d4af9e69SDag-Erling Smørgrav * and let the output queue drain. 1707d4af9e69SDag-Erling Smørgrav */ 1708bc5531deSDag-Erling Smørgrav r = sshbuf_check_reserve(oqueue, SFTP_MAX_MSG_LENGTH); 1709bc5531deSDag-Erling Smørgrav if (r == 0) 1710b66f2d16SKris Kennaway process(); 1711bc5531deSDag-Erling Smørgrav else if (r != SSH_ERR_NO_BUFFER_SPACE) 1712bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_check_reserve: %s", 1713bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1714b66f2d16SKris Kennaway } 1715b66f2d16SKris Kennaway } 1716