1*bc5531deSDag-Erling Smørgrav /* $OpenBSD: sftp-server.c,v 1.105 2015/01/20 23:14:00 deraadt 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 20*bc5531deSDag-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 <pwd.h> 44761efaa7SDag-Erling Smørgrav #include <time.h> 45761efaa7SDag-Erling Smørgrav #include <unistd.h> 46761efaa7SDag-Erling Smørgrav #include <stdarg.h> 47761efaa7SDag-Erling Smørgrav 48b66f2d16SKris Kennaway #include "xmalloc.h" 49*bc5531deSDag-Erling Smørgrav #include "sshbuf.h" 50*bc5531deSDag-Erling Smørgrav #include "ssherr.h" 51761efaa7SDag-Erling Smørgrav #include "log.h" 52021d409fSDag-Erling Smørgrav #include "misc.h" 53f7167e0eSDag-Erling Smørgrav #include "match.h" 54761efaa7SDag-Erling Smørgrav #include "uidswap.h" 55b66f2d16SKris Kennaway 561e8db6e2SBrian Feldman #include "sftp.h" 571e8db6e2SBrian Feldman #include "sftp-common.h" 58b66f2d16SKris Kennaway 59761efaa7SDag-Erling Smørgrav /* Our verbosity */ 60f7167e0eSDag-Erling Smørgrav static LogLevel log_level = SYSLOG_LEVEL_ERROR; 61761efaa7SDag-Erling Smørgrav 62761efaa7SDag-Erling Smørgrav /* Our client */ 63f7167e0eSDag-Erling Smørgrav static struct passwd *pw = NULL; 64f7167e0eSDag-Erling Smørgrav static char *client_addr = NULL; 6583d2307dSDag-Erling Smørgrav 66b66f2d16SKris Kennaway /* input and output queue */ 67*bc5531deSDag-Erling Smørgrav struct sshbuf *iqueue; 68*bc5531deSDag-Erling Smørgrav struct sshbuf *oqueue; 69b66f2d16SKris Kennaway 701e8db6e2SBrian Feldman /* Version of client */ 71f7167e0eSDag-Erling Smørgrav static u_int version; 72f7167e0eSDag-Erling Smørgrav 73f7167e0eSDag-Erling Smørgrav /* SSH2_FXP_INIT received */ 74f7167e0eSDag-Erling Smørgrav static int init_done; 751e8db6e2SBrian Feldman 76b15c8340SDag-Erling Smørgrav /* Disable writes */ 77f7167e0eSDag-Erling Smørgrav static int readonly; 78f7167e0eSDag-Erling Smørgrav 79f7167e0eSDag-Erling Smørgrav /* Requests that are allowed/denied */ 80f7167e0eSDag-Erling Smørgrav static char *request_whitelist, *request_blacklist; 81b15c8340SDag-Erling Smørgrav 82d95e11bfSDag-Erling Smørgrav /* portable attributes, etc. */ 83b66f2d16SKris Kennaway typedef struct Stat Stat; 84b66f2d16SKris Kennaway 851e8db6e2SBrian Feldman struct Stat { 86b66f2d16SKris Kennaway char *name; 87b66f2d16SKris Kennaway char *long_name; 88b66f2d16SKris Kennaway Attrib attrib; 89b66f2d16SKris Kennaway }; 90b66f2d16SKris Kennaway 91f7167e0eSDag-Erling Smørgrav /* Packet handlers */ 92f7167e0eSDag-Erling Smørgrav static void process_open(u_int32_t id); 93f7167e0eSDag-Erling Smørgrav static void process_close(u_int32_t id); 94f7167e0eSDag-Erling Smørgrav static void process_read(u_int32_t id); 95f7167e0eSDag-Erling Smørgrav static void process_write(u_int32_t id); 96f7167e0eSDag-Erling Smørgrav static void process_stat(u_int32_t id); 97f7167e0eSDag-Erling Smørgrav static void process_lstat(u_int32_t id); 98f7167e0eSDag-Erling Smørgrav static void process_fstat(u_int32_t id); 99f7167e0eSDag-Erling Smørgrav static void process_setstat(u_int32_t id); 100f7167e0eSDag-Erling Smørgrav static void process_fsetstat(u_int32_t id); 101f7167e0eSDag-Erling Smørgrav static void process_opendir(u_int32_t id); 102f7167e0eSDag-Erling Smørgrav static void process_readdir(u_int32_t id); 103f7167e0eSDag-Erling Smørgrav static void process_remove(u_int32_t id); 104f7167e0eSDag-Erling Smørgrav static void process_mkdir(u_int32_t id); 105f7167e0eSDag-Erling Smørgrav static void process_rmdir(u_int32_t id); 106f7167e0eSDag-Erling Smørgrav static void process_realpath(u_int32_t id); 107f7167e0eSDag-Erling Smørgrav static void process_rename(u_int32_t id); 108f7167e0eSDag-Erling Smørgrav static void process_readlink(u_int32_t id); 109f7167e0eSDag-Erling Smørgrav static void process_symlink(u_int32_t id); 110f7167e0eSDag-Erling Smørgrav static void process_extended_posix_rename(u_int32_t id); 111f7167e0eSDag-Erling Smørgrav static void process_extended_statvfs(u_int32_t id); 112f7167e0eSDag-Erling Smørgrav static void process_extended_fstatvfs(u_int32_t id); 113f7167e0eSDag-Erling Smørgrav static void process_extended_hardlink(u_int32_t id); 114f7167e0eSDag-Erling Smørgrav static void process_extended_fsync(u_int32_t id); 115f7167e0eSDag-Erling Smørgrav static void process_extended(u_int32_t id); 116f7167e0eSDag-Erling Smørgrav 117f7167e0eSDag-Erling Smørgrav struct sftp_handler { 118f7167e0eSDag-Erling Smørgrav const char *name; /* user-visible name for fine-grained perms */ 119f7167e0eSDag-Erling Smørgrav const char *ext_name; /* extended request name */ 120f7167e0eSDag-Erling Smørgrav u_int type; /* packet type, for non extended packets */ 121f7167e0eSDag-Erling Smørgrav void (*handler)(u_int32_t); 122f7167e0eSDag-Erling Smørgrav int does_write; /* if nonzero, banned for readonly mode */ 123f7167e0eSDag-Erling Smørgrav }; 124f7167e0eSDag-Erling Smørgrav 125f7167e0eSDag-Erling Smørgrav struct sftp_handler handlers[] = { 126f7167e0eSDag-Erling Smørgrav /* NB. SSH2_FXP_OPEN does the readonly check in the handler itself */ 127f7167e0eSDag-Erling Smørgrav { "open", NULL, SSH2_FXP_OPEN, process_open, 0 }, 128f7167e0eSDag-Erling Smørgrav { "close", NULL, SSH2_FXP_CLOSE, process_close, 0 }, 129f7167e0eSDag-Erling Smørgrav { "read", NULL, SSH2_FXP_READ, process_read, 0 }, 130f7167e0eSDag-Erling Smørgrav { "write", NULL, SSH2_FXP_WRITE, process_write, 1 }, 131f7167e0eSDag-Erling Smørgrav { "lstat", NULL, SSH2_FXP_LSTAT, process_lstat, 0 }, 132f7167e0eSDag-Erling Smørgrav { "fstat", NULL, SSH2_FXP_FSTAT, process_fstat, 0 }, 133f7167e0eSDag-Erling Smørgrav { "setstat", NULL, SSH2_FXP_SETSTAT, process_setstat, 1 }, 134f7167e0eSDag-Erling Smørgrav { "fsetstat", NULL, SSH2_FXP_FSETSTAT, process_fsetstat, 1 }, 135f7167e0eSDag-Erling Smørgrav { "opendir", NULL, SSH2_FXP_OPENDIR, process_opendir, 0 }, 136f7167e0eSDag-Erling Smørgrav { "readdir", NULL, SSH2_FXP_READDIR, process_readdir, 0 }, 137f7167e0eSDag-Erling Smørgrav { "remove", NULL, SSH2_FXP_REMOVE, process_remove, 1 }, 138f7167e0eSDag-Erling Smørgrav { "mkdir", NULL, SSH2_FXP_MKDIR, process_mkdir, 1 }, 139f7167e0eSDag-Erling Smørgrav { "rmdir", NULL, SSH2_FXP_RMDIR, process_rmdir, 1 }, 140f7167e0eSDag-Erling Smørgrav { "realpath", NULL, SSH2_FXP_REALPATH, process_realpath, 0 }, 141f7167e0eSDag-Erling Smørgrav { "stat", NULL, SSH2_FXP_STAT, process_stat, 0 }, 142f7167e0eSDag-Erling Smørgrav { "rename", NULL, SSH2_FXP_RENAME, process_rename, 1 }, 143f7167e0eSDag-Erling Smørgrav { "readlink", NULL, SSH2_FXP_READLINK, process_readlink, 0 }, 144f7167e0eSDag-Erling Smørgrav { "symlink", NULL, SSH2_FXP_SYMLINK, process_symlink, 1 }, 145f7167e0eSDag-Erling Smørgrav { NULL, NULL, 0, NULL, 0 } 146f7167e0eSDag-Erling Smørgrav }; 147f7167e0eSDag-Erling Smørgrav 148f7167e0eSDag-Erling Smørgrav /* SSH2_FXP_EXTENDED submessages */ 149f7167e0eSDag-Erling Smørgrav struct sftp_handler extended_handlers[] = { 150f7167e0eSDag-Erling Smørgrav { "posix-rename", "posix-rename@openssh.com", 0, 151f7167e0eSDag-Erling Smørgrav process_extended_posix_rename, 1 }, 152f7167e0eSDag-Erling Smørgrav { "statvfs", "statvfs@openssh.com", 0, process_extended_statvfs, 0 }, 153f7167e0eSDag-Erling Smørgrav { "fstatvfs", "fstatvfs@openssh.com", 0, process_extended_fstatvfs, 0 }, 154f7167e0eSDag-Erling Smørgrav { "hardlink", "hardlink@openssh.com", 0, process_extended_hardlink, 1 }, 155f7167e0eSDag-Erling Smørgrav { "fsync", "fsync@openssh.com", 0, process_extended_fsync, 1 }, 156f7167e0eSDag-Erling Smørgrav { NULL, NULL, 0, NULL, 0 } 157f7167e0eSDag-Erling Smørgrav }; 158f7167e0eSDag-Erling Smørgrav 159f7167e0eSDag-Erling Smørgrav static int 160f7167e0eSDag-Erling Smørgrav request_permitted(struct sftp_handler *h) 161f7167e0eSDag-Erling Smørgrav { 162f7167e0eSDag-Erling Smørgrav char *result; 163f7167e0eSDag-Erling Smørgrav 164f7167e0eSDag-Erling Smørgrav if (readonly && h->does_write) { 165f7167e0eSDag-Erling Smørgrav verbose("Refusing %s request in read-only mode", h->name); 166f7167e0eSDag-Erling Smørgrav return 0; 167f7167e0eSDag-Erling Smørgrav } 168f7167e0eSDag-Erling Smørgrav if (request_blacklist != NULL && 169f7167e0eSDag-Erling Smørgrav ((result = match_list(h->name, request_blacklist, NULL))) != NULL) { 170f7167e0eSDag-Erling Smørgrav free(result); 171f7167e0eSDag-Erling Smørgrav verbose("Refusing blacklisted %s request", h->name); 172f7167e0eSDag-Erling Smørgrav return 0; 173f7167e0eSDag-Erling Smørgrav } 174f7167e0eSDag-Erling Smørgrav if (request_whitelist != NULL && 175f7167e0eSDag-Erling Smørgrav ((result = match_list(h->name, request_whitelist, NULL))) != NULL) { 176f7167e0eSDag-Erling Smørgrav free(result); 177f7167e0eSDag-Erling Smørgrav debug2("Permitting whitelisted %s request", h->name); 178f7167e0eSDag-Erling Smørgrav return 1; 179f7167e0eSDag-Erling Smørgrav } 180f7167e0eSDag-Erling Smørgrav if (request_whitelist != NULL) { 181f7167e0eSDag-Erling Smørgrav verbose("Refusing non-whitelisted %s request", h->name); 182f7167e0eSDag-Erling Smørgrav return 0; 183f7167e0eSDag-Erling Smørgrav } 184f7167e0eSDag-Erling Smørgrav return 1; 185f7167e0eSDag-Erling Smørgrav } 186f7167e0eSDag-Erling Smørgrav 187ae1f160dSDag-Erling Smørgrav static int 188b66f2d16SKris Kennaway errno_to_portable(int unixerrno) 189b66f2d16SKris Kennaway { 190b66f2d16SKris Kennaway int ret = 0; 1911e8db6e2SBrian Feldman 192b66f2d16SKris Kennaway switch (unixerrno) { 193b66f2d16SKris Kennaway case 0: 1941e8db6e2SBrian Feldman ret = SSH2_FX_OK; 195b66f2d16SKris Kennaway break; 196b66f2d16SKris Kennaway case ENOENT: 197b66f2d16SKris Kennaway case ENOTDIR: 198b66f2d16SKris Kennaway case EBADF: 199b66f2d16SKris Kennaway case ELOOP: 2001e8db6e2SBrian Feldman ret = SSH2_FX_NO_SUCH_FILE; 201b66f2d16SKris Kennaway break; 202b66f2d16SKris Kennaway case EPERM: 203b66f2d16SKris Kennaway case EACCES: 204b66f2d16SKris Kennaway case EFAULT: 2051e8db6e2SBrian Feldman ret = SSH2_FX_PERMISSION_DENIED; 206b66f2d16SKris Kennaway break; 207b66f2d16SKris Kennaway case ENAMETOOLONG: 208b66f2d16SKris Kennaway case EINVAL: 2091e8db6e2SBrian Feldman ret = SSH2_FX_BAD_MESSAGE; 210b66f2d16SKris Kennaway break; 211d4af9e69SDag-Erling Smørgrav case ENOSYS: 212d4af9e69SDag-Erling Smørgrav ret = SSH2_FX_OP_UNSUPPORTED; 213d4af9e69SDag-Erling Smørgrav break; 214b66f2d16SKris Kennaway default: 2151e8db6e2SBrian Feldman ret = SSH2_FX_FAILURE; 216b66f2d16SKris Kennaway break; 217b66f2d16SKris Kennaway } 218b66f2d16SKris Kennaway return ret; 219b66f2d16SKris Kennaway } 220b66f2d16SKris Kennaway 221ae1f160dSDag-Erling Smørgrav static int 222b66f2d16SKris Kennaway flags_from_portable(int pflags) 223b66f2d16SKris Kennaway { 224b66f2d16SKris Kennaway int flags = 0; 2251e8db6e2SBrian Feldman 2261e8db6e2SBrian Feldman if ((pflags & SSH2_FXF_READ) && 2271e8db6e2SBrian Feldman (pflags & SSH2_FXF_WRITE)) { 228b66f2d16SKris Kennaway flags = O_RDWR; 2291e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_READ) { 230b66f2d16SKris Kennaway flags = O_RDONLY; 2311e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_WRITE) { 232b66f2d16SKris Kennaway flags = O_WRONLY; 233b66f2d16SKris Kennaway } 234f7167e0eSDag-Erling Smørgrav if (pflags & SSH2_FXF_APPEND) 235f7167e0eSDag-Erling Smørgrav flags |= O_APPEND; 2361e8db6e2SBrian Feldman if (pflags & SSH2_FXF_CREAT) 237b66f2d16SKris Kennaway flags |= O_CREAT; 2381e8db6e2SBrian Feldman if (pflags & SSH2_FXF_TRUNC) 239b66f2d16SKris Kennaway flags |= O_TRUNC; 2401e8db6e2SBrian Feldman if (pflags & SSH2_FXF_EXCL) 241b66f2d16SKris Kennaway flags |= O_EXCL; 242b66f2d16SKris Kennaway return flags; 243b66f2d16SKris Kennaway } 244b66f2d16SKris Kennaway 245761efaa7SDag-Erling Smørgrav static const char * 246761efaa7SDag-Erling Smørgrav string_from_portable(int pflags) 247761efaa7SDag-Erling Smørgrav { 248761efaa7SDag-Erling Smørgrav static char ret[128]; 249761efaa7SDag-Erling Smørgrav 250761efaa7SDag-Erling Smørgrav *ret = '\0'; 251761efaa7SDag-Erling Smørgrav 252761efaa7SDag-Erling Smørgrav #define PAPPEND(str) { \ 253761efaa7SDag-Erling Smørgrav if (*ret != '\0') \ 254761efaa7SDag-Erling Smørgrav strlcat(ret, ",", sizeof(ret)); \ 255761efaa7SDag-Erling Smørgrav strlcat(ret, str, sizeof(ret)); \ 256761efaa7SDag-Erling Smørgrav } 257761efaa7SDag-Erling Smørgrav 258761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_READ) 259761efaa7SDag-Erling Smørgrav PAPPEND("READ") 260761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_WRITE) 261761efaa7SDag-Erling Smørgrav PAPPEND("WRITE") 262f7167e0eSDag-Erling Smørgrav if (pflags & SSH2_FXF_APPEND) 263f7167e0eSDag-Erling Smørgrav PAPPEND("APPEND") 264761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_CREAT) 265761efaa7SDag-Erling Smørgrav PAPPEND("CREATE") 266761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_TRUNC) 267761efaa7SDag-Erling Smørgrav PAPPEND("TRUNCATE") 268761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_EXCL) 269761efaa7SDag-Erling Smørgrav PAPPEND("EXCL") 270761efaa7SDag-Erling Smørgrav 271761efaa7SDag-Erling Smørgrav return ret; 272761efaa7SDag-Erling Smørgrav } 273761efaa7SDag-Erling Smørgrav 274b66f2d16SKris Kennaway /* handle handles */ 275b66f2d16SKris Kennaway 276b66f2d16SKris Kennaway typedef struct Handle Handle; 277b66f2d16SKris Kennaway struct Handle { 278b66f2d16SKris Kennaway int use; 279b66f2d16SKris Kennaway DIR *dirp; 280b66f2d16SKris Kennaway int fd; 281f7167e0eSDag-Erling Smørgrav int flags; 282b66f2d16SKris Kennaway char *name; 283761efaa7SDag-Erling Smørgrav u_int64_t bytes_read, bytes_write; 284d4af9e69SDag-Erling Smørgrav int next_unused; 285b66f2d16SKris Kennaway }; 2861e8db6e2SBrian Feldman 287b66f2d16SKris Kennaway enum { 288b66f2d16SKris Kennaway HANDLE_UNUSED, 289b66f2d16SKris Kennaway HANDLE_DIR, 290b66f2d16SKris Kennaway HANDLE_FILE 291b66f2d16SKris Kennaway }; 2921e8db6e2SBrian Feldman 293d4af9e69SDag-Erling Smørgrav Handle *handles = NULL; 294d4af9e69SDag-Erling Smørgrav u_int num_handles = 0; 295d4af9e69SDag-Erling Smørgrav int first_unused_handle = -1; 296b66f2d16SKris Kennaway 297d4af9e69SDag-Erling Smørgrav static void handle_unused(int i) 298b66f2d16SKris Kennaway { 299b66f2d16SKris Kennaway handles[i].use = HANDLE_UNUSED; 300d4af9e69SDag-Erling Smørgrav handles[i].next_unused = first_unused_handle; 301d4af9e69SDag-Erling Smørgrav first_unused_handle = i; 302b66f2d16SKris Kennaway } 303b66f2d16SKris Kennaway 304ae1f160dSDag-Erling Smørgrav static int 305f7167e0eSDag-Erling Smørgrav handle_new(int use, const char *name, int fd, int flags, DIR *dirp) 306b66f2d16SKris Kennaway { 307d4af9e69SDag-Erling Smørgrav int i; 3081e8db6e2SBrian Feldman 309d4af9e69SDag-Erling Smørgrav if (first_unused_handle == -1) { 310d4af9e69SDag-Erling Smørgrav if (num_handles + 1 <= num_handles) 311d4af9e69SDag-Erling Smørgrav return -1; 312d4af9e69SDag-Erling Smørgrav num_handles++; 313d4af9e69SDag-Erling Smørgrav handles = xrealloc(handles, num_handles, sizeof(Handle)); 314d4af9e69SDag-Erling Smørgrav handle_unused(num_handles - 1); 315d4af9e69SDag-Erling Smørgrav } 316d4af9e69SDag-Erling Smørgrav 317d4af9e69SDag-Erling Smørgrav i = first_unused_handle; 318d4af9e69SDag-Erling Smørgrav first_unused_handle = handles[i].next_unused; 319d4af9e69SDag-Erling Smørgrav 320b66f2d16SKris Kennaway handles[i].use = use; 321b66f2d16SKris Kennaway handles[i].dirp = dirp; 322b66f2d16SKris Kennaway handles[i].fd = fd; 323f7167e0eSDag-Erling Smørgrav handles[i].flags = flags; 324d0c8c0bcSDag-Erling Smørgrav handles[i].name = xstrdup(name); 325761efaa7SDag-Erling Smørgrav handles[i].bytes_read = handles[i].bytes_write = 0; 326d4af9e69SDag-Erling Smørgrav 327b66f2d16SKris Kennaway return i; 328b66f2d16SKris Kennaway } 329b66f2d16SKris Kennaway 330ae1f160dSDag-Erling Smørgrav static int 331b66f2d16SKris Kennaway handle_is_ok(int i, int type) 332b66f2d16SKris Kennaway { 333d4af9e69SDag-Erling Smørgrav return i >= 0 && (u_int)i < num_handles && handles[i].use == type; 334b66f2d16SKris Kennaway } 335b66f2d16SKris Kennaway 336ae1f160dSDag-Erling Smørgrav static int 337*bc5531deSDag-Erling Smørgrav handle_to_string(int handle, u_char **stringp, int *hlenp) 338b66f2d16SKris Kennaway { 339b66f2d16SKris Kennaway if (stringp == NULL || hlenp == NULL) 340b66f2d16SKris Kennaway return -1; 3411e8db6e2SBrian Feldman *stringp = xmalloc(sizeof(int32_t)); 342761efaa7SDag-Erling Smørgrav put_u32(*stringp, handle); 3431e8db6e2SBrian Feldman *hlenp = sizeof(int32_t); 344b66f2d16SKris Kennaway return 0; 345b66f2d16SKris Kennaway } 346b66f2d16SKris Kennaway 347ae1f160dSDag-Erling Smørgrav static int 348*bc5531deSDag-Erling Smørgrav handle_from_string(const u_char *handle, u_int hlen) 349b66f2d16SKris Kennaway { 3501e8db6e2SBrian Feldman int val; 3511e8db6e2SBrian Feldman 3521e8db6e2SBrian Feldman if (hlen != sizeof(int32_t)) 353b66f2d16SKris Kennaway return -1; 354761efaa7SDag-Erling Smørgrav val = get_u32(handle); 355b66f2d16SKris Kennaway if (handle_is_ok(val, HANDLE_FILE) || 356b66f2d16SKris Kennaway handle_is_ok(val, HANDLE_DIR)) 357b66f2d16SKris Kennaway return val; 358b66f2d16SKris Kennaway return -1; 359b66f2d16SKris Kennaway } 360b66f2d16SKris Kennaway 361ae1f160dSDag-Erling Smørgrav static char * 362b66f2d16SKris Kennaway handle_to_name(int handle) 363b66f2d16SKris Kennaway { 364b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)|| 365b66f2d16SKris Kennaway handle_is_ok(handle, HANDLE_FILE)) 366b66f2d16SKris Kennaway return handles[handle].name; 367b66f2d16SKris Kennaway return NULL; 368b66f2d16SKris Kennaway } 369b66f2d16SKris Kennaway 370ae1f160dSDag-Erling Smørgrav static DIR * 371b66f2d16SKris Kennaway handle_to_dir(int handle) 372b66f2d16SKris Kennaway { 373b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)) 374b66f2d16SKris Kennaway return handles[handle].dirp; 375b66f2d16SKris Kennaway return NULL; 376b66f2d16SKris Kennaway } 377b66f2d16SKris Kennaway 378ae1f160dSDag-Erling Smørgrav static int 379b66f2d16SKris Kennaway handle_to_fd(int handle) 380b66f2d16SKris Kennaway { 381b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) 382b66f2d16SKris Kennaway return handles[handle].fd; 383b66f2d16SKris Kennaway return -1; 384b66f2d16SKris Kennaway } 385b66f2d16SKris Kennaway 386f7167e0eSDag-Erling Smørgrav static int 387f7167e0eSDag-Erling Smørgrav handle_to_flags(int handle) 388f7167e0eSDag-Erling Smørgrav { 389f7167e0eSDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 390f7167e0eSDag-Erling Smørgrav return handles[handle].flags; 391f7167e0eSDag-Erling Smørgrav return 0; 392f7167e0eSDag-Erling Smørgrav } 393f7167e0eSDag-Erling Smørgrav 394761efaa7SDag-Erling Smørgrav static void 395761efaa7SDag-Erling Smørgrav handle_update_read(int handle, ssize_t bytes) 396761efaa7SDag-Erling Smørgrav { 397761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 398761efaa7SDag-Erling Smørgrav handles[handle].bytes_read += bytes; 399761efaa7SDag-Erling Smørgrav } 400761efaa7SDag-Erling Smørgrav 401761efaa7SDag-Erling Smørgrav static void 402761efaa7SDag-Erling Smørgrav handle_update_write(int handle, ssize_t bytes) 403761efaa7SDag-Erling Smørgrav { 404761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 405761efaa7SDag-Erling Smørgrav handles[handle].bytes_write += bytes; 406761efaa7SDag-Erling Smørgrav } 407761efaa7SDag-Erling Smørgrav 408761efaa7SDag-Erling Smørgrav static u_int64_t 409761efaa7SDag-Erling Smørgrav handle_bytes_read(int handle) 410761efaa7SDag-Erling Smørgrav { 411761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 412761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_read); 413761efaa7SDag-Erling Smørgrav return 0; 414761efaa7SDag-Erling Smørgrav } 415761efaa7SDag-Erling Smørgrav 416761efaa7SDag-Erling Smørgrav static u_int64_t 417761efaa7SDag-Erling Smørgrav handle_bytes_write(int handle) 418761efaa7SDag-Erling Smørgrav { 419761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 420761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_write); 421761efaa7SDag-Erling Smørgrav return 0; 422761efaa7SDag-Erling Smørgrav } 423761efaa7SDag-Erling Smørgrav 424ae1f160dSDag-Erling Smørgrav static int 425b66f2d16SKris Kennaway handle_close(int handle) 426b66f2d16SKris Kennaway { 427b66f2d16SKris Kennaway int ret = -1; 4281e8db6e2SBrian Feldman 429b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) { 430b66f2d16SKris Kennaway ret = close(handles[handle].fd); 431e4a9863fSDag-Erling Smørgrav free(handles[handle].name); 432d4af9e69SDag-Erling Smørgrav handle_unused(handle); 433b66f2d16SKris Kennaway } else if (handle_is_ok(handle, HANDLE_DIR)) { 434b66f2d16SKris Kennaway ret = closedir(handles[handle].dirp); 435e4a9863fSDag-Erling Smørgrav free(handles[handle].name); 436d4af9e69SDag-Erling Smørgrav handle_unused(handle); 437b66f2d16SKris Kennaway } else { 438b66f2d16SKris Kennaway errno = ENOENT; 439b66f2d16SKris Kennaway } 440b66f2d16SKris Kennaway return ret; 441b66f2d16SKris Kennaway } 442b66f2d16SKris Kennaway 443761efaa7SDag-Erling Smørgrav static void 444761efaa7SDag-Erling Smørgrav handle_log_close(int handle, char *emsg) 445761efaa7SDag-Erling Smørgrav { 446761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) { 447761efaa7SDag-Erling Smørgrav logit("%s%sclose \"%s\" bytes read %llu written %llu", 448761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 449761efaa7SDag-Erling Smørgrav handle_to_name(handle), 450d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_read(handle), 451d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_write(handle)); 452761efaa7SDag-Erling Smørgrav } else { 453761efaa7SDag-Erling Smørgrav logit("%s%sclosedir \"%s\"", 454761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 455761efaa7SDag-Erling Smørgrav handle_to_name(handle)); 456761efaa7SDag-Erling Smørgrav } 457761efaa7SDag-Erling Smørgrav } 458761efaa7SDag-Erling Smørgrav 459761efaa7SDag-Erling Smørgrav static void 460761efaa7SDag-Erling Smørgrav handle_log_exit(void) 461761efaa7SDag-Erling Smørgrav { 462761efaa7SDag-Erling Smørgrav u_int i; 463761efaa7SDag-Erling Smørgrav 464d4af9e69SDag-Erling Smørgrav for (i = 0; i < num_handles; i++) 465761efaa7SDag-Erling Smørgrav if (handles[i].use != HANDLE_UNUSED) 466761efaa7SDag-Erling Smørgrav handle_log_close(i, "forced"); 467761efaa7SDag-Erling Smørgrav } 468761efaa7SDag-Erling Smørgrav 469ae1f160dSDag-Erling Smørgrav static int 470*bc5531deSDag-Erling Smørgrav get_handle(struct sshbuf *queue, int *hp) 471b66f2d16SKris Kennaway { 472*bc5531deSDag-Erling Smørgrav u_char *handle; 473*bc5531deSDag-Erling Smørgrav int r; 474*bc5531deSDag-Erling Smørgrav size_t hlen; 4751e8db6e2SBrian Feldman 476*bc5531deSDag-Erling Smørgrav *hp = -1; 477*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_string(queue, &handle, &hlen)) != 0) 478*bc5531deSDag-Erling Smørgrav return r; 4791e8db6e2SBrian Feldman if (hlen < 256) 480*bc5531deSDag-Erling Smørgrav *hp = handle_from_string(handle, hlen); 481e4a9863fSDag-Erling Smørgrav free(handle); 482*bc5531deSDag-Erling Smørgrav return 0; 483b66f2d16SKris Kennaway } 484b66f2d16SKris Kennaway 485b66f2d16SKris Kennaway /* send replies */ 486b66f2d16SKris Kennaway 487ae1f160dSDag-Erling Smørgrav static void 488*bc5531deSDag-Erling Smørgrav send_msg(struct sshbuf *m) 489b66f2d16SKris Kennaway { 490*bc5531deSDag-Erling Smørgrav int r; 4911e8db6e2SBrian Feldman 492*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_stringb(oqueue, m)) != 0) 493*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 494*bc5531deSDag-Erling Smørgrav sshbuf_reset(m); 495b66f2d16SKris Kennaway } 496b66f2d16SKris Kennaway 497761efaa7SDag-Erling Smørgrav static const char * 498761efaa7SDag-Erling Smørgrav status_to_message(u_int32_t status) 499b66f2d16SKris Kennaway { 5001e8db6e2SBrian Feldman const char *status_messages[] = { 5011e8db6e2SBrian Feldman "Success", /* SSH_FX_OK */ 5021e8db6e2SBrian Feldman "End of file", /* SSH_FX_EOF */ 5031e8db6e2SBrian Feldman "No such file", /* SSH_FX_NO_SUCH_FILE */ 5041e8db6e2SBrian Feldman "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 5051e8db6e2SBrian Feldman "Failure", /* SSH_FX_FAILURE */ 5061e8db6e2SBrian Feldman "Bad message", /* SSH_FX_BAD_MESSAGE */ 5071e8db6e2SBrian Feldman "No connection", /* SSH_FX_NO_CONNECTION */ 5081e8db6e2SBrian Feldman "Connection lost", /* SSH_FX_CONNECTION_LOST */ 5091e8db6e2SBrian Feldman "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 5101e8db6e2SBrian Feldman "Unknown error" /* Others */ 5111e8db6e2SBrian Feldman }; 512761efaa7SDag-Erling Smørgrav return (status_messages[MIN(status,SSH2_FX_MAX)]); 513761efaa7SDag-Erling Smørgrav } 5141e8db6e2SBrian Feldman 515761efaa7SDag-Erling Smørgrav static void 516761efaa7SDag-Erling Smørgrav send_status(u_int32_t id, u_int32_t status) 517761efaa7SDag-Erling Smørgrav { 518*bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 519*bc5531deSDag-Erling Smørgrav int r; 520761efaa7SDag-Erling Smørgrav 521761efaa7SDag-Erling Smørgrav debug3("request %u: sent status %u", id, status); 522761efaa7SDag-Erling Smørgrav if (log_level > SYSLOG_LEVEL_VERBOSE || 523761efaa7SDag-Erling Smørgrav (status != SSH2_FX_OK && status != SSH2_FX_EOF)) 524761efaa7SDag-Erling Smørgrav logit("sent status %s", status_to_message(status)); 525*bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 526*bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 527*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_STATUS)) != 0 || 528*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 529*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, status)) != 0) 530*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 5311e8db6e2SBrian Feldman if (version >= 3) { 532*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(msg, 533*bc5531deSDag-Erling Smørgrav status_to_message(status))) != 0 || 534*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "")) != 0) 535*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 5361e8db6e2SBrian Feldman } 537*bc5531deSDag-Erling Smørgrav send_msg(msg); 538*bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 539b66f2d16SKris Kennaway } 540ae1f160dSDag-Erling Smørgrav static void 541*bc5531deSDag-Erling Smørgrav send_data_or_handle(char type, u_int32_t id, const u_char *data, int dlen) 542b66f2d16SKris Kennaway { 543*bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 544*bc5531deSDag-Erling Smørgrav int r; 5451e8db6e2SBrian Feldman 546*bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 547*bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 548*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, type)) != 0 || 549*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 550*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_string(msg, data, dlen)) != 0) 551*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 552*bc5531deSDag-Erling Smørgrav send_msg(msg); 553*bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 554b66f2d16SKris Kennaway } 555b66f2d16SKris Kennaway 556ae1f160dSDag-Erling Smørgrav static void 557*bc5531deSDag-Erling Smørgrav send_data(u_int32_t id, const u_char *data, int dlen) 558b66f2d16SKris Kennaway { 559761efaa7SDag-Erling Smørgrav debug("request %u: sent data len %d", id, dlen); 5601e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 561b66f2d16SKris Kennaway } 562b66f2d16SKris Kennaway 563ae1f160dSDag-Erling Smørgrav static void 564b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle) 565b66f2d16SKris Kennaway { 566*bc5531deSDag-Erling Smørgrav u_char *string; 567b66f2d16SKris Kennaway int hlen; 5681e8db6e2SBrian Feldman 569b66f2d16SKris Kennaway handle_to_string(handle, &string, &hlen); 570761efaa7SDag-Erling Smørgrav debug("request %u: sent handle handle %d", id, handle); 5711e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 572e4a9863fSDag-Erling Smørgrav free(string); 573b66f2d16SKris Kennaway } 574b66f2d16SKris Kennaway 575ae1f160dSDag-Erling Smørgrav static void 576efcad6b7SDag-Erling Smørgrav send_names(u_int32_t id, int count, const Stat *stats) 577b66f2d16SKris Kennaway { 578*bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 579*bc5531deSDag-Erling Smørgrav int i, r; 5801e8db6e2SBrian Feldman 581*bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 582*bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 583*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_NAME)) != 0 || 584*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 585*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, count)) != 0) 586*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 587761efaa7SDag-Erling Smørgrav debug("request %u: sent names count %d", id, count); 588b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 589*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(msg, stats[i].name)) != 0 || 590*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, stats[i].long_name)) != 0 || 591*bc5531deSDag-Erling Smørgrav (r = encode_attrib(msg, &stats[i].attrib)) != 0) 592*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 593b66f2d16SKris Kennaway } 594*bc5531deSDag-Erling Smørgrav send_msg(msg); 595*bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 596b66f2d16SKris Kennaway } 597b66f2d16SKris Kennaway 598ae1f160dSDag-Erling Smørgrav static void 599efcad6b7SDag-Erling Smørgrav send_attrib(u_int32_t id, const Attrib *a) 600b66f2d16SKris Kennaway { 601*bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 602*bc5531deSDag-Erling Smørgrav int r; 6031e8db6e2SBrian Feldman 604761efaa7SDag-Erling Smørgrav debug("request %u: sent attrib have 0x%x", id, a->flags); 605*bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 606*bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 607*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_ATTRS)) != 0 || 608*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 609*bc5531deSDag-Erling Smørgrav (r = encode_attrib(msg, a)) != 0) 610*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 611*bc5531deSDag-Erling Smørgrav send_msg(msg); 612*bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 613b66f2d16SKris Kennaway } 614b66f2d16SKris Kennaway 615d4af9e69SDag-Erling Smørgrav static void 616d4af9e69SDag-Erling Smørgrav send_statvfs(u_int32_t id, struct statvfs *st) 617d4af9e69SDag-Erling Smørgrav { 618*bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 619d4af9e69SDag-Erling Smørgrav u_int64_t flag; 620*bc5531deSDag-Erling Smørgrav int r; 621d4af9e69SDag-Erling Smørgrav 622d4af9e69SDag-Erling Smørgrav flag = (st->f_flag & ST_RDONLY) ? SSH2_FXE_STATVFS_ST_RDONLY : 0; 623d4af9e69SDag-Erling Smørgrav flag |= (st->f_flag & ST_NOSUID) ? SSH2_FXE_STATVFS_ST_NOSUID : 0; 624d4af9e69SDag-Erling Smørgrav 625*bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 626*bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 627*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED_REPLY)) != 0 || 628*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 629*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_bsize)) != 0 || 630*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_frsize)) != 0 || 631*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_blocks)) != 0 || 632*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_bfree)) != 0 || 633*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_bavail)) != 0 || 634*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_files)) != 0 || 635*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_ffree)) != 0 || 636*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_favail)) != 0 || 637*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, FSID_TO_ULONG(st->f_fsid))) != 0 || 638*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, flag)) != 0 || 639*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_namemax)) != 0) 640*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 641*bc5531deSDag-Erling Smørgrav send_msg(msg); 642*bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 643d4af9e69SDag-Erling Smørgrav } 644d4af9e69SDag-Erling Smørgrav 645b66f2d16SKris Kennaway /* parse incoming */ 646b66f2d16SKris Kennaway 647ae1f160dSDag-Erling Smørgrav static void 648b66f2d16SKris Kennaway process_init(void) 649b66f2d16SKris Kennaway { 650*bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 651*bc5531deSDag-Erling Smørgrav int r; 652b66f2d16SKris Kennaway 653*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(iqueue, &version)) != 0) 654*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 655e146993eSDag-Erling Smørgrav verbose("received client version %u", version); 656*bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 657*bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 658*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_VERSION)) != 0 || 659*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, SSH2_FILEXFER_VERSION)) != 0 || 660d4af9e69SDag-Erling Smørgrav /* POSIX rename extension */ 661*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "posix-rename@openssh.com")) != 0 || 662*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "1")) != 0 || /* version */ 663d4af9e69SDag-Erling Smørgrav /* statvfs extension */ 664*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "statvfs@openssh.com")) != 0 || 665*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "2")) != 0 || /* version */ 666d4af9e69SDag-Erling Smørgrav /* fstatvfs extension */ 667*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "fstatvfs@openssh.com")) != 0 || 668*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "2")) != 0 || /* version */ 6694a421b63SDag-Erling Smørgrav /* hardlink extension */ 670*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "hardlink@openssh.com")) != 0 || 671*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "1")) != 0 || /* version */ 672f7167e0eSDag-Erling Smørgrav /* fsync extension */ 673*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "fsync@openssh.com")) != 0 || 674*bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "1")) != 0) /* version */ 675*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 676*bc5531deSDag-Erling Smørgrav send_msg(msg); 677*bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 678b66f2d16SKris Kennaway } 679b66f2d16SKris Kennaway 680ae1f160dSDag-Erling Smørgrav static void 681f7167e0eSDag-Erling Smørgrav process_open(u_int32_t id) 682b66f2d16SKris Kennaway { 683f7167e0eSDag-Erling Smørgrav u_int32_t pflags; 684*bc5531deSDag-Erling Smørgrav Attrib a; 685b66f2d16SKris Kennaway char *name; 686*bc5531deSDag-Erling Smørgrav int r, handle, fd, flags, mode, status = SSH2_FX_FAILURE; 687b66f2d16SKris Kennaway 688*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 689*bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u32(iqueue, &pflags)) != 0 || /* portable flags */ 690*bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 691*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 692*bc5531deSDag-Erling Smørgrav 693761efaa7SDag-Erling Smørgrav debug3("request %u: open flags %d", id, pflags); 694b66f2d16SKris Kennaway flags = flags_from_portable(pflags); 695*bc5531deSDag-Erling Smørgrav mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a.perm : 0666; 696761efaa7SDag-Erling Smørgrav logit("open \"%s\" flags %s mode 0%o", 697761efaa7SDag-Erling Smørgrav name, string_from_portable(pflags), mode); 698b15c8340SDag-Erling Smørgrav if (readonly && 699f7167e0eSDag-Erling Smørgrav ((flags & O_ACCMODE) == O_WRONLY || 700f7167e0eSDag-Erling Smørgrav (flags & O_ACCMODE) == O_RDWR)) { 701f7167e0eSDag-Erling Smørgrav verbose("Refusing open request in read-only mode"); 702b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 703f7167e0eSDag-Erling Smørgrav } else { 704b66f2d16SKris Kennaway fd = open(name, flags, mode); 705b66f2d16SKris Kennaway if (fd < 0) { 706b66f2d16SKris Kennaway status = errno_to_portable(errno); 707b66f2d16SKris Kennaway } else { 708f7167e0eSDag-Erling Smørgrav handle = handle_new(HANDLE_FILE, name, fd, flags, NULL); 709b66f2d16SKris Kennaway if (handle < 0) { 710b66f2d16SKris Kennaway close(fd); 711b66f2d16SKris Kennaway } else { 712b66f2d16SKris Kennaway send_handle(id, handle); 7131e8db6e2SBrian Feldman status = SSH2_FX_OK; 714b66f2d16SKris Kennaway } 715b66f2d16SKris Kennaway } 716b15c8340SDag-Erling Smørgrav } 7171e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 718b66f2d16SKris Kennaway send_status(id, status); 719e4a9863fSDag-Erling Smørgrav free(name); 720b66f2d16SKris Kennaway } 721b66f2d16SKris Kennaway 722ae1f160dSDag-Erling Smørgrav static void 723f7167e0eSDag-Erling Smørgrav process_close(u_int32_t id) 724b66f2d16SKris Kennaway { 725*bc5531deSDag-Erling Smørgrav int r, handle, ret, status = SSH2_FX_FAILURE; 726b66f2d16SKris Kennaway 727*bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 728*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 729*bc5531deSDag-Erling Smørgrav 730761efaa7SDag-Erling Smørgrav debug3("request %u: close handle %u", id, handle); 731761efaa7SDag-Erling Smørgrav handle_log_close(handle, NULL); 732b66f2d16SKris Kennaway ret = handle_close(handle); 7331e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 734b66f2d16SKris Kennaway send_status(id, status); 735b66f2d16SKris Kennaway } 736b66f2d16SKris Kennaway 737ae1f160dSDag-Erling Smørgrav static void 738f7167e0eSDag-Erling Smørgrav process_read(u_int32_t id) 739b66f2d16SKris Kennaway { 740*bc5531deSDag-Erling Smørgrav u_char buf[64*1024]; 741f7167e0eSDag-Erling Smørgrav u_int32_t len; 742*bc5531deSDag-Erling Smørgrav int r, handle, fd, ret, status = SSH2_FX_FAILURE; 743b66f2d16SKris Kennaway u_int64_t off; 744b66f2d16SKris Kennaway 745*bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0 || 746*bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u64(iqueue, &off)) != 0 || 747*bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u32(iqueue, &len)) != 0) 748*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 749b66f2d16SKris Kennaway 750761efaa7SDag-Erling Smørgrav debug("request %u: read \"%s\" (handle %d) off %llu len %d", 751761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len); 752b66f2d16SKris Kennaway if (len > sizeof buf) { 753b66f2d16SKris Kennaway len = sizeof buf; 754761efaa7SDag-Erling Smørgrav debug2("read change len %d", len); 755b66f2d16SKris Kennaway } 756b66f2d16SKris Kennaway fd = handle_to_fd(handle); 757b66f2d16SKris Kennaway if (fd >= 0) { 758b66f2d16SKris Kennaway if (lseek(fd, off, SEEK_SET) < 0) { 759b66f2d16SKris Kennaway error("process_read: seek failed"); 760b66f2d16SKris Kennaway status = errno_to_portable(errno); 761b66f2d16SKris Kennaway } else { 762b66f2d16SKris Kennaway ret = read(fd, buf, len); 763b66f2d16SKris Kennaway if (ret < 0) { 764b66f2d16SKris Kennaway status = errno_to_portable(errno); 765b66f2d16SKris Kennaway } else if (ret == 0) { 7661e8db6e2SBrian Feldman status = SSH2_FX_EOF; 767b66f2d16SKris Kennaway } else { 768b66f2d16SKris Kennaway send_data(id, buf, ret); 7691e8db6e2SBrian Feldman status = SSH2_FX_OK; 770761efaa7SDag-Erling Smørgrav handle_update_read(handle, ret); 771b66f2d16SKris Kennaway } 772b66f2d16SKris Kennaway } 773b66f2d16SKris Kennaway } 7741e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 775b66f2d16SKris Kennaway send_status(id, status); 776b66f2d16SKris Kennaway } 777b66f2d16SKris Kennaway 778ae1f160dSDag-Erling Smørgrav static void 779f7167e0eSDag-Erling Smørgrav process_write(u_int32_t id) 780b66f2d16SKris Kennaway { 781b66f2d16SKris Kennaway u_int64_t off; 782*bc5531deSDag-Erling Smørgrav size_t len; 783*bc5531deSDag-Erling Smørgrav int r, handle, fd, ret, status; 784*bc5531deSDag-Erling Smørgrav u_char *data; 785b66f2d16SKris Kennaway 786*bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0 || 787*bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u64(iqueue, &off)) != 0 || 788*bc5531deSDag-Erling Smørgrav (r = sshbuf_get_string(iqueue, &data, &len)) != 0) 789*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 790b66f2d16SKris Kennaway 791*bc5531deSDag-Erling Smørgrav debug("request %u: write \"%s\" (handle %d) off %llu len %zu", 792761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len); 793b66f2d16SKris Kennaway fd = handle_to_fd(handle); 794b15c8340SDag-Erling Smørgrav 795b15c8340SDag-Erling Smørgrav if (fd < 0) 796b15c8340SDag-Erling Smørgrav status = SSH2_FX_FAILURE; 797b15c8340SDag-Erling Smørgrav else { 798f7167e0eSDag-Erling Smørgrav if (!(handle_to_flags(handle) & O_APPEND) && 799f7167e0eSDag-Erling Smørgrav lseek(fd, off, SEEK_SET) < 0) { 800b66f2d16SKris Kennaway status = errno_to_portable(errno); 801b66f2d16SKris Kennaway error("process_write: seek failed"); 802b66f2d16SKris Kennaway } else { 803b66f2d16SKris Kennaway /* XXX ATOMICIO ? */ 804b66f2d16SKris Kennaway ret = write(fd, data, len); 805043840dfSDag-Erling Smørgrav if (ret < 0) { 806b66f2d16SKris Kennaway error("process_write: write failed"); 807b66f2d16SKris Kennaway status = errno_to_portable(errno); 808043840dfSDag-Erling Smørgrav } else if ((size_t)ret == len) { 8091e8db6e2SBrian Feldman status = SSH2_FX_OK; 810761efaa7SDag-Erling Smørgrav handle_update_write(handle, ret); 811b66f2d16SKris Kennaway } else { 812761efaa7SDag-Erling Smørgrav debug2("nothing at all written"); 813b15c8340SDag-Erling Smørgrav status = SSH2_FX_FAILURE; 814b66f2d16SKris Kennaway } 815b66f2d16SKris Kennaway } 816b66f2d16SKris Kennaway } 817b66f2d16SKris Kennaway send_status(id, status); 818e4a9863fSDag-Erling Smørgrav free(data); 819b66f2d16SKris Kennaway } 820b66f2d16SKris Kennaway 821ae1f160dSDag-Erling Smørgrav static void 822f7167e0eSDag-Erling Smørgrav process_do_stat(u_int32_t id, int do_lstat) 823b66f2d16SKris Kennaway { 8241e8db6e2SBrian Feldman Attrib a; 825b66f2d16SKris Kennaway struct stat st; 826b66f2d16SKris Kennaway char *name; 827*bc5531deSDag-Erling Smørgrav int r, status = SSH2_FX_FAILURE; 828b66f2d16SKris Kennaway 829*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) 830*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 831*bc5531deSDag-Erling Smørgrav 832761efaa7SDag-Erling Smørgrav debug3("request %u: %sstat", id, do_lstat ? "l" : ""); 833761efaa7SDag-Erling Smørgrav verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); 834*bc5531deSDag-Erling Smørgrav r = do_lstat ? lstat(name, &st) : stat(name, &st); 835*bc5531deSDag-Erling Smørgrav if (r < 0) { 836b66f2d16SKris Kennaway status = errno_to_portable(errno); 837b66f2d16SKris Kennaway } else { 8381e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 8391e8db6e2SBrian Feldman send_attrib(id, &a); 8401e8db6e2SBrian Feldman status = SSH2_FX_OK; 841b66f2d16SKris Kennaway } 8421e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 843b66f2d16SKris Kennaway send_status(id, status); 844e4a9863fSDag-Erling Smørgrav free(name); 845b66f2d16SKris Kennaway } 846b66f2d16SKris Kennaway 847ae1f160dSDag-Erling Smørgrav static void 848f7167e0eSDag-Erling Smørgrav process_stat(u_int32_t id) 849b66f2d16SKris Kennaway { 850f7167e0eSDag-Erling Smørgrav process_do_stat(id, 0); 851b66f2d16SKris Kennaway } 852b66f2d16SKris Kennaway 853ae1f160dSDag-Erling Smørgrav static void 854f7167e0eSDag-Erling Smørgrav process_lstat(u_int32_t id) 855b66f2d16SKris Kennaway { 856f7167e0eSDag-Erling Smørgrav process_do_stat(id, 1); 857b66f2d16SKris Kennaway } 858b66f2d16SKris Kennaway 859ae1f160dSDag-Erling Smørgrav static void 860f7167e0eSDag-Erling Smørgrav process_fstat(u_int32_t id) 861b66f2d16SKris Kennaway { 8621e8db6e2SBrian Feldman Attrib a; 863b66f2d16SKris Kennaway struct stat st; 864*bc5531deSDag-Erling Smørgrav int fd, r, handle, status = SSH2_FX_FAILURE; 865b66f2d16SKris Kennaway 866*bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 867*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 868761efaa7SDag-Erling Smørgrav debug("request %u: fstat \"%s\" (handle %u)", 869761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle); 870b66f2d16SKris Kennaway fd = handle_to_fd(handle); 871b66f2d16SKris Kennaway if (fd >= 0) { 872*bc5531deSDag-Erling Smørgrav r = fstat(fd, &st); 873*bc5531deSDag-Erling Smørgrav if (r < 0) { 874b66f2d16SKris Kennaway status = errno_to_portable(errno); 875b66f2d16SKris Kennaway } else { 8761e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 8771e8db6e2SBrian Feldman send_attrib(id, &a); 8781e8db6e2SBrian Feldman status = SSH2_FX_OK; 879b66f2d16SKris Kennaway } 880b66f2d16SKris Kennaway } 8811e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 882b66f2d16SKris Kennaway send_status(id, status); 883b66f2d16SKris Kennaway } 884b66f2d16SKris Kennaway 885ae1f160dSDag-Erling Smørgrav static struct timeval * 886efcad6b7SDag-Erling Smørgrav attrib_to_tv(const Attrib *a) 887b66f2d16SKris Kennaway { 888b66f2d16SKris Kennaway static struct timeval tv[2]; 8891e8db6e2SBrian Feldman 890b66f2d16SKris Kennaway tv[0].tv_sec = a->atime; 891b66f2d16SKris Kennaway tv[0].tv_usec = 0; 892b66f2d16SKris Kennaway tv[1].tv_sec = a->mtime; 893b66f2d16SKris Kennaway tv[1].tv_usec = 0; 894b66f2d16SKris Kennaway return tv; 895b66f2d16SKris Kennaway } 896b66f2d16SKris Kennaway 897ae1f160dSDag-Erling Smørgrav static void 898f7167e0eSDag-Erling Smørgrav process_setstat(u_int32_t id) 899b66f2d16SKris Kennaway { 900*bc5531deSDag-Erling Smørgrav Attrib a; 901b66f2d16SKris Kennaway char *name; 902*bc5531deSDag-Erling Smørgrav int r, status = SSH2_FX_OK; 903b66f2d16SKris Kennaway 904*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 905*bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 906*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 907*bc5531deSDag-Erling Smørgrav 908761efaa7SDag-Erling Smørgrav debug("request %u: setstat name \"%s\"", id, name); 909*bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { 910d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu", 911*bc5531deSDag-Erling Smørgrav name, (unsigned long long)a.size); 912*bc5531deSDag-Erling Smørgrav r = truncate(name, a.size); 913*bc5531deSDag-Erling Smørgrav if (r == -1) 914ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 915ae1f160dSDag-Erling Smørgrav } 916*bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 917*bc5531deSDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a.perm); 918*bc5531deSDag-Erling Smørgrav r = chmod(name, a.perm & 07777); 919*bc5531deSDag-Erling Smørgrav if (r == -1) 920b66f2d16SKris Kennaway status = errno_to_portable(errno); 921b66f2d16SKris Kennaway } 922*bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 923761efaa7SDag-Erling Smørgrav char buf[64]; 924*bc5531deSDag-Erling Smørgrav time_t t = a.mtime; 925761efaa7SDag-Erling Smørgrav 926761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 927761efaa7SDag-Erling Smørgrav localtime(&t)); 928761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf); 929*bc5531deSDag-Erling Smørgrav r = utimes(name, attrib_to_tv(&a)); 930*bc5531deSDag-Erling Smørgrav if (r == -1) 931b66f2d16SKris Kennaway status = errno_to_portable(errno); 932b66f2d16SKris Kennaway } 933*bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { 934761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name, 935*bc5531deSDag-Erling Smørgrav (u_long)a.uid, (u_long)a.gid); 936*bc5531deSDag-Erling Smørgrav r = chown(name, a.uid, a.gid); 937*bc5531deSDag-Erling Smørgrav if (r == -1) 9381e8db6e2SBrian Feldman status = errno_to_portable(errno); 9391e8db6e2SBrian Feldman } 940b66f2d16SKris Kennaway send_status(id, status); 941e4a9863fSDag-Erling Smørgrav free(name); 942b66f2d16SKris Kennaway } 943b66f2d16SKris Kennaway 944ae1f160dSDag-Erling Smørgrav static void 945f7167e0eSDag-Erling Smørgrav process_fsetstat(u_int32_t id) 946b66f2d16SKris Kennaway { 947*bc5531deSDag-Erling Smørgrav Attrib a; 948*bc5531deSDag-Erling Smørgrav int handle, fd, r; 9491e8db6e2SBrian Feldman int status = SSH2_FX_OK; 950b66f2d16SKris Kennaway 951*bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0 || 952*bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 953*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 954*bc5531deSDag-Erling Smørgrav 955761efaa7SDag-Erling Smørgrav debug("request %u: fsetstat handle %d", id, handle); 956b66f2d16SKris Kennaway fd = handle_to_fd(handle); 957b15c8340SDag-Erling Smørgrav if (fd < 0) 9581e8db6e2SBrian Feldman status = SSH2_FX_FAILURE; 959b15c8340SDag-Erling Smørgrav else { 960761efaa7SDag-Erling Smørgrav char *name = handle_to_name(handle); 961761efaa7SDag-Erling Smørgrav 962*bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { 963d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu", 964*bc5531deSDag-Erling Smørgrav name, (unsigned long long)a.size); 965*bc5531deSDag-Erling Smørgrav r = ftruncate(fd, a.size); 966*bc5531deSDag-Erling Smørgrav if (r == -1) 967ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 968ae1f160dSDag-Erling Smørgrav } 969*bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 970*bc5531deSDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a.perm); 97183d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 972*bc5531deSDag-Erling Smørgrav r = fchmod(fd, a.perm & 07777); 97383d2307dSDag-Erling Smørgrav #else 974*bc5531deSDag-Erling Smørgrav r = chmod(name, a.perm & 07777); 97583d2307dSDag-Erling Smørgrav #endif 976*bc5531deSDag-Erling Smørgrav if (r == -1) 977b66f2d16SKris Kennaway status = errno_to_portable(errno); 978b66f2d16SKris Kennaway } 979*bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 980761efaa7SDag-Erling Smørgrav char buf[64]; 981*bc5531deSDag-Erling Smørgrav time_t t = a.mtime; 982761efaa7SDag-Erling Smørgrav 983761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 984761efaa7SDag-Erling Smørgrav localtime(&t)); 985761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf); 98683d2307dSDag-Erling Smørgrav #ifdef HAVE_FUTIMES 987*bc5531deSDag-Erling Smørgrav r = futimes(fd, attrib_to_tv(&a)); 98883d2307dSDag-Erling Smørgrav #else 989*bc5531deSDag-Erling Smørgrav r = utimes(name, attrib_to_tv(&a)); 99083d2307dSDag-Erling Smørgrav #endif 991*bc5531deSDag-Erling Smørgrav if (r == -1) 992b66f2d16SKris Kennaway status = errno_to_portable(errno); 993b66f2d16SKris Kennaway } 994*bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { 995761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name, 996*bc5531deSDag-Erling Smørgrav (u_long)a.uid, (u_long)a.gid); 99783d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHOWN 998*bc5531deSDag-Erling Smørgrav r = fchown(fd, a.uid, a.gid); 99983d2307dSDag-Erling Smørgrav #else 1000*bc5531deSDag-Erling Smørgrav r = chown(name, a.uid, a.gid); 100183d2307dSDag-Erling Smørgrav #endif 1002*bc5531deSDag-Erling Smørgrav if (r == -1) 10031e8db6e2SBrian Feldman status = errno_to_portable(errno); 10041e8db6e2SBrian Feldman } 1005b66f2d16SKris Kennaway } 1006b66f2d16SKris Kennaway send_status(id, status); 1007b66f2d16SKris Kennaway } 1008b66f2d16SKris Kennaway 1009ae1f160dSDag-Erling Smørgrav static void 1010f7167e0eSDag-Erling Smørgrav process_opendir(u_int32_t id) 1011b66f2d16SKris Kennaway { 1012b66f2d16SKris Kennaway DIR *dirp = NULL; 1013b66f2d16SKris Kennaway char *path; 1014*bc5531deSDag-Erling Smørgrav int r, handle, status = SSH2_FX_FAILURE; 1015b66f2d16SKris Kennaway 1016*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 1017*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1018*bc5531deSDag-Erling Smørgrav 1019761efaa7SDag-Erling Smørgrav debug3("request %u: opendir", id); 1020761efaa7SDag-Erling Smørgrav logit("opendir \"%s\"", path); 1021b66f2d16SKris Kennaway dirp = opendir(path); 1022b66f2d16SKris Kennaway if (dirp == NULL) { 1023b66f2d16SKris Kennaway status = errno_to_portable(errno); 1024b66f2d16SKris Kennaway } else { 1025f7167e0eSDag-Erling Smørgrav handle = handle_new(HANDLE_DIR, path, 0, 0, dirp); 1026b66f2d16SKris Kennaway if (handle < 0) { 1027b66f2d16SKris Kennaway closedir(dirp); 1028b66f2d16SKris Kennaway } else { 1029b66f2d16SKris Kennaway send_handle(id, handle); 10301e8db6e2SBrian Feldman status = SSH2_FX_OK; 1031b66f2d16SKris Kennaway } 1032b66f2d16SKris Kennaway 1033b66f2d16SKris Kennaway } 10341e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 1035b66f2d16SKris Kennaway send_status(id, status); 1036e4a9863fSDag-Erling Smørgrav free(path); 1037b66f2d16SKris Kennaway } 1038b66f2d16SKris Kennaway 1039ae1f160dSDag-Erling Smørgrav static void 1040f7167e0eSDag-Erling Smørgrav process_readdir(u_int32_t id) 1041b66f2d16SKris Kennaway { 1042b66f2d16SKris Kennaway DIR *dirp; 1043b66f2d16SKris Kennaway struct dirent *dp; 1044b66f2d16SKris Kennaway char *path; 1045*bc5531deSDag-Erling Smørgrav int r, handle; 1046b66f2d16SKris Kennaway 1047*bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 1048*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1049*bc5531deSDag-Erling Smørgrav 1050761efaa7SDag-Erling Smørgrav debug("request %u: readdir \"%s\" (handle %d)", id, 1051761efaa7SDag-Erling Smørgrav handle_to_name(handle), handle); 1052b66f2d16SKris Kennaway dirp = handle_to_dir(handle); 1053b66f2d16SKris Kennaway path = handle_to_name(handle); 1054b66f2d16SKris Kennaway if (dirp == NULL || path == NULL) { 10551e8db6e2SBrian Feldman send_status(id, SSH2_FX_FAILURE); 1056b66f2d16SKris Kennaway } else { 1057b66f2d16SKris Kennaway struct stat st; 1058*bc5531deSDag-Erling Smørgrav char pathname[PATH_MAX]; 1059b66f2d16SKris Kennaway Stat *stats; 1060b66f2d16SKris Kennaway int nstats = 10, count = 0, i; 1061ee21a45fSDag-Erling Smørgrav 1062761efaa7SDag-Erling Smørgrav stats = xcalloc(nstats, sizeof(Stat)); 1063b66f2d16SKris Kennaway while ((dp = readdir(dirp)) != NULL) { 1064b66f2d16SKris Kennaway if (count >= nstats) { 1065b66f2d16SKris Kennaway nstats *= 2; 1066761efaa7SDag-Erling Smørgrav stats = xrealloc(stats, nstats, sizeof(Stat)); 1067b66f2d16SKris Kennaway } 1068b66f2d16SKris Kennaway /* XXX OVERFLOW ? */ 1069ae1f160dSDag-Erling Smørgrav snprintf(pathname, sizeof pathname, "%s%s%s", path, 1070ae1f160dSDag-Erling Smørgrav strcmp(path, "/") ? "/" : "", dp->d_name); 1071b66f2d16SKris Kennaway if (lstat(pathname, &st) < 0) 1072b66f2d16SKris Kennaway continue; 10731e8db6e2SBrian Feldman stat_to_attrib(&st, &(stats[count].attrib)); 1074b66f2d16SKris Kennaway stats[count].name = xstrdup(dp->d_name); 1075b15c8340SDag-Erling Smørgrav stats[count].long_name = ls_file(dp->d_name, &st, 0, 0); 1076b66f2d16SKris Kennaway count++; 1077b66f2d16SKris Kennaway /* send up to 100 entries in one message */ 10781e8db6e2SBrian Feldman /* XXX check packet size instead */ 1079b66f2d16SKris Kennaway if (count == 100) 1080b66f2d16SKris Kennaway break; 1081b66f2d16SKris Kennaway } 10821e8db6e2SBrian Feldman if (count > 0) { 1083b66f2d16SKris Kennaway send_names(id, count, stats); 1084b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 1085e4a9863fSDag-Erling Smørgrav free(stats[i].name); 1086e4a9863fSDag-Erling Smørgrav free(stats[i].long_name); 1087b66f2d16SKris Kennaway } 10881e8db6e2SBrian Feldman } else { 10891e8db6e2SBrian Feldman send_status(id, SSH2_FX_EOF); 10901e8db6e2SBrian Feldman } 1091e4a9863fSDag-Erling Smørgrav free(stats); 1092b66f2d16SKris Kennaway } 1093b66f2d16SKris Kennaway } 1094b66f2d16SKris Kennaway 1095ae1f160dSDag-Erling Smørgrav static void 1096f7167e0eSDag-Erling Smørgrav process_remove(u_int32_t id) 1097b66f2d16SKris Kennaway { 1098b66f2d16SKris Kennaway char *name; 1099*bc5531deSDag-Erling Smørgrav int r, status = SSH2_FX_FAILURE; 1100b66f2d16SKris Kennaway 1101*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) 1102*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1103*bc5531deSDag-Erling Smørgrav 1104761efaa7SDag-Erling Smørgrav debug3("request %u: remove", id); 1105761efaa7SDag-Erling Smørgrav logit("remove name \"%s\"", name); 1106*bc5531deSDag-Erling Smørgrav r = unlink(name); 1107*bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1108b66f2d16SKris Kennaway send_status(id, status); 1109e4a9863fSDag-Erling Smørgrav free(name); 1110b66f2d16SKris Kennaway } 1111b66f2d16SKris Kennaway 1112ae1f160dSDag-Erling Smørgrav static void 1113f7167e0eSDag-Erling Smørgrav process_mkdir(u_int32_t id) 1114b66f2d16SKris Kennaway { 1115*bc5531deSDag-Erling Smørgrav Attrib a; 1116b66f2d16SKris Kennaway char *name; 1117*bc5531deSDag-Erling Smørgrav int r, mode, status = SSH2_FX_FAILURE; 1118b66f2d16SKris Kennaway 1119*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 1120*bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 1121*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1122*bc5531deSDag-Erling Smørgrav 1123*bc5531deSDag-Erling Smørgrav mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 1124*bc5531deSDag-Erling Smørgrav a.perm & 07777 : 0777; 1125761efaa7SDag-Erling Smørgrav debug3("request %u: mkdir", id); 1126761efaa7SDag-Erling Smørgrav logit("mkdir name \"%s\" mode 0%o", name, mode); 1127*bc5531deSDag-Erling Smørgrav r = mkdir(name, mode); 1128*bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1129b66f2d16SKris Kennaway send_status(id, status); 1130e4a9863fSDag-Erling Smørgrav free(name); 1131b66f2d16SKris Kennaway } 1132b66f2d16SKris Kennaway 1133ae1f160dSDag-Erling Smørgrav static void 1134f7167e0eSDag-Erling Smørgrav process_rmdir(u_int32_t id) 1135b66f2d16SKris Kennaway { 1136b66f2d16SKris Kennaway char *name; 1137*bc5531deSDag-Erling Smørgrav int r, status; 1138b66f2d16SKris Kennaway 1139*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) 1140*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1141*bc5531deSDag-Erling Smørgrav 1142761efaa7SDag-Erling Smørgrav debug3("request %u: rmdir", id); 1143761efaa7SDag-Erling Smørgrav logit("rmdir name \"%s\"", name); 1144*bc5531deSDag-Erling Smørgrav r = rmdir(name); 1145*bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1146b66f2d16SKris Kennaway send_status(id, status); 1147e4a9863fSDag-Erling Smørgrav free(name); 1148b66f2d16SKris Kennaway } 1149b66f2d16SKris Kennaway 1150ae1f160dSDag-Erling Smørgrav static void 1151f7167e0eSDag-Erling Smørgrav process_realpath(u_int32_t id) 1152b66f2d16SKris Kennaway { 1153*bc5531deSDag-Erling Smørgrav char resolvedname[PATH_MAX]; 1154b66f2d16SKris Kennaway char *path; 1155*bc5531deSDag-Erling Smørgrav int r; 1156b66f2d16SKris Kennaway 1157*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 1158*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1159*bc5531deSDag-Erling Smørgrav 11601e8db6e2SBrian Feldman if (path[0] == '\0') { 1161e4a9863fSDag-Erling Smørgrav free(path); 11621e8db6e2SBrian Feldman path = xstrdup("."); 11631e8db6e2SBrian Feldman } 1164761efaa7SDag-Erling Smørgrav debug3("request %u: realpath", id); 1165761efaa7SDag-Erling Smørgrav verbose("realpath \"%s\"", path); 1166b66f2d16SKris Kennaway if (realpath(path, resolvedname) == NULL) { 1167b66f2d16SKris Kennaway send_status(id, errno_to_portable(errno)); 1168b66f2d16SKris Kennaway } else { 1169b66f2d16SKris Kennaway Stat s; 1170b66f2d16SKris Kennaway attrib_clear(&s.attrib); 1171b66f2d16SKris Kennaway s.name = s.long_name = resolvedname; 1172b66f2d16SKris Kennaway send_names(id, 1, &s); 1173b66f2d16SKris Kennaway } 1174e4a9863fSDag-Erling Smørgrav free(path); 1175b66f2d16SKris Kennaway } 1176b66f2d16SKris Kennaway 1177ae1f160dSDag-Erling Smørgrav static void 1178f7167e0eSDag-Erling Smørgrav process_rename(u_int32_t id) 1179b66f2d16SKris Kennaway { 1180b66f2d16SKris Kennaway char *oldpath, *newpath; 1181*bc5531deSDag-Erling Smørgrav int r, status; 1182d0c8c0bcSDag-Erling Smørgrav struct stat sb; 1183b66f2d16SKris Kennaway 1184*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1185*bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 1186*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1187*bc5531deSDag-Erling Smørgrav 1188761efaa7SDag-Erling Smørgrav debug3("request %u: rename", id); 1189761efaa7SDag-Erling Smørgrav logit("rename old \"%s\" new \"%s\"", oldpath, newpath); 1190d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_FAILURE; 1191f7167e0eSDag-Erling Smørgrav if (lstat(oldpath, &sb) == -1) 1192d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1193d0c8c0bcSDag-Erling Smørgrav else if (S_ISREG(sb.st_mode)) { 1194d0c8c0bcSDag-Erling Smørgrav /* Race-free rename of regular files */ 1195d74d50a8SDag-Erling Smørgrav if (link(oldpath, newpath) == -1) { 11967aee6ffeSDag-Erling Smørgrav if (errno == EOPNOTSUPP || errno == ENOSYS 1197d4af9e69SDag-Erling Smørgrav #ifdef EXDEV 1198d4af9e69SDag-Erling Smørgrav || errno == EXDEV 1199d4af9e69SDag-Erling Smørgrav #endif 1200d74d50a8SDag-Erling Smørgrav #ifdef LINK_OPNOTSUPP_ERRNO 1201d74d50a8SDag-Erling Smørgrav || errno == LINK_OPNOTSUPP_ERRNO 1202d74d50a8SDag-Erling Smørgrav #endif 1203d74d50a8SDag-Erling Smørgrav ) { 1204d74d50a8SDag-Erling Smørgrav struct stat st; 1205d74d50a8SDag-Erling Smørgrav 1206d74d50a8SDag-Erling Smørgrav /* 1207d74d50a8SDag-Erling Smørgrav * fs doesn't support links, so fall back to 1208d74d50a8SDag-Erling Smørgrav * stat+rename. This is racy. 1209d74d50a8SDag-Erling Smørgrav */ 1210d74d50a8SDag-Erling Smørgrav if (stat(newpath, &st) == -1) { 1211d74d50a8SDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1212d74d50a8SDag-Erling Smørgrav status = 1213d74d50a8SDag-Erling Smørgrav errno_to_portable(errno); 1214d74d50a8SDag-Erling Smørgrav else 1215d74d50a8SDag-Erling Smørgrav status = SSH2_FX_OK; 1216d74d50a8SDag-Erling Smørgrav } 1217d74d50a8SDag-Erling Smørgrav } else { 1218d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1219d74d50a8SDag-Erling Smørgrav } 1220d74d50a8SDag-Erling Smørgrav } else if (unlink(oldpath) == -1) { 1221d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1222d0c8c0bcSDag-Erling Smørgrav /* clean spare link */ 1223d0c8c0bcSDag-Erling Smørgrav unlink(newpath); 1224d0c8c0bcSDag-Erling Smørgrav } else 1225d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 1226d0c8c0bcSDag-Erling Smørgrav } else if (stat(newpath, &sb) == -1) { 1227d0c8c0bcSDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1228d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1229d0c8c0bcSDag-Erling Smørgrav else 1230d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 12311e8db6e2SBrian Feldman } 1232b66f2d16SKris Kennaway send_status(id, status); 1233e4a9863fSDag-Erling Smørgrav free(oldpath); 1234e4a9863fSDag-Erling Smørgrav free(newpath); 1235b66f2d16SKris Kennaway } 1236b66f2d16SKris Kennaway 1237ae1f160dSDag-Erling Smørgrav static void 1238f7167e0eSDag-Erling Smørgrav process_readlink(u_int32_t id) 12391e8db6e2SBrian Feldman { 1240*bc5531deSDag-Erling Smørgrav int r, len; 1241*bc5531deSDag-Erling Smørgrav char buf[PATH_MAX]; 12421e8db6e2SBrian Feldman char *path; 12431e8db6e2SBrian Feldman 1244*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 1245*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1246*bc5531deSDag-Erling Smørgrav 1247761efaa7SDag-Erling Smørgrav debug3("request %u: readlink", id); 1248761efaa7SDag-Erling Smørgrav verbose("readlink \"%s\"", path); 1249d74d50a8SDag-Erling Smørgrav if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) 12501e8db6e2SBrian Feldman send_status(id, errno_to_portable(errno)); 12511e8db6e2SBrian Feldman else { 12521e8db6e2SBrian Feldman Stat s; 12531e8db6e2SBrian Feldman 1254d74d50a8SDag-Erling Smørgrav buf[len] = '\0'; 12551e8db6e2SBrian Feldman attrib_clear(&s.attrib); 1256d74d50a8SDag-Erling Smørgrav s.name = s.long_name = buf; 12571e8db6e2SBrian Feldman send_names(id, 1, &s); 12581e8db6e2SBrian Feldman } 1259e4a9863fSDag-Erling Smørgrav free(path); 12601e8db6e2SBrian Feldman } 12611e8db6e2SBrian Feldman 1262ae1f160dSDag-Erling Smørgrav static void 1263f7167e0eSDag-Erling Smørgrav process_symlink(u_int32_t id) 12641e8db6e2SBrian Feldman { 12651e8db6e2SBrian Feldman char *oldpath, *newpath; 1266*bc5531deSDag-Erling Smørgrav int r, status; 12671e8db6e2SBrian Feldman 1268*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1269*bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 1270*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1271*bc5531deSDag-Erling Smørgrav 1272761efaa7SDag-Erling Smørgrav debug3("request %u: symlink", id); 1273761efaa7SDag-Erling Smørgrav logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); 1274d0c8c0bcSDag-Erling Smørgrav /* this will fail if 'newpath' exists */ 1275*bc5531deSDag-Erling Smørgrav r = symlink(oldpath, newpath); 1276*bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 12771e8db6e2SBrian Feldman send_status(id, status); 1278e4a9863fSDag-Erling Smørgrav free(oldpath); 1279e4a9863fSDag-Erling Smørgrav free(newpath); 12801e8db6e2SBrian Feldman } 12811e8db6e2SBrian Feldman 1282ae1f160dSDag-Erling Smørgrav static void 1283d4af9e69SDag-Erling Smørgrav process_extended_posix_rename(u_int32_t id) 1284d4af9e69SDag-Erling Smørgrav { 1285d4af9e69SDag-Erling Smørgrav char *oldpath, *newpath; 1286*bc5531deSDag-Erling Smørgrav int r, status; 1287d4af9e69SDag-Erling Smørgrav 1288*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1289*bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 1290*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1291*bc5531deSDag-Erling Smørgrav 1292d4af9e69SDag-Erling Smørgrav debug3("request %u: posix-rename", id); 1293d4af9e69SDag-Erling Smørgrav logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); 1294*bc5531deSDag-Erling Smørgrav r = rename(oldpath, newpath); 1295*bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1296b15c8340SDag-Erling Smørgrav send_status(id, status); 1297e4a9863fSDag-Erling Smørgrav free(oldpath); 1298e4a9863fSDag-Erling Smørgrav free(newpath); 1299d4af9e69SDag-Erling Smørgrav } 1300d4af9e69SDag-Erling Smørgrav 1301d4af9e69SDag-Erling Smørgrav static void 1302d4af9e69SDag-Erling Smørgrav process_extended_statvfs(u_int32_t id) 1303d4af9e69SDag-Erling Smørgrav { 1304d4af9e69SDag-Erling Smørgrav char *path; 1305d4af9e69SDag-Erling Smørgrav struct statvfs st; 1306*bc5531deSDag-Erling Smørgrav int r; 1307d4af9e69SDag-Erling Smørgrav 1308*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 1309*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1310f7167e0eSDag-Erling Smørgrav debug3("request %u: statvfs", id); 1311f7167e0eSDag-Erling Smørgrav logit("statvfs \"%s\"", path); 1312d4af9e69SDag-Erling Smørgrav 1313d4af9e69SDag-Erling Smørgrav if (statvfs(path, &st) != 0) 1314d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1315d4af9e69SDag-Erling Smørgrav else 1316d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st); 1317e4a9863fSDag-Erling Smørgrav free(path); 1318d4af9e69SDag-Erling Smørgrav } 1319d4af9e69SDag-Erling Smørgrav 1320d4af9e69SDag-Erling Smørgrav static void 1321d4af9e69SDag-Erling Smørgrav process_extended_fstatvfs(u_int32_t id) 1322d4af9e69SDag-Erling Smørgrav { 1323*bc5531deSDag-Erling Smørgrav int r, handle, fd; 1324d4af9e69SDag-Erling Smørgrav struct statvfs st; 1325d4af9e69SDag-Erling Smørgrav 1326*bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 1327*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1328d4af9e69SDag-Erling Smørgrav debug("request %u: fstatvfs \"%s\" (handle %u)", 1329d4af9e69SDag-Erling Smørgrav id, handle_to_name(handle), handle); 1330d4af9e69SDag-Erling Smørgrav if ((fd = handle_to_fd(handle)) < 0) { 1331d4af9e69SDag-Erling Smørgrav send_status(id, SSH2_FX_FAILURE); 1332d4af9e69SDag-Erling Smørgrav return; 1333d4af9e69SDag-Erling Smørgrav } 1334d4af9e69SDag-Erling Smørgrav if (fstatvfs(fd, &st) != 0) 1335d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1336d4af9e69SDag-Erling Smørgrav else 1337d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st); 1338d4af9e69SDag-Erling Smørgrav } 1339d4af9e69SDag-Erling Smørgrav 1340d4af9e69SDag-Erling Smørgrav static void 13414a421b63SDag-Erling Smørgrav process_extended_hardlink(u_int32_t id) 13424a421b63SDag-Erling Smørgrav { 13434a421b63SDag-Erling Smørgrav char *oldpath, *newpath; 1344*bc5531deSDag-Erling Smørgrav int r, status; 13454a421b63SDag-Erling Smørgrav 1346*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1347*bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 1348*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1349*bc5531deSDag-Erling Smørgrav 13504a421b63SDag-Erling Smørgrav debug3("request %u: hardlink", id); 13514a421b63SDag-Erling Smørgrav logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath); 1352*bc5531deSDag-Erling Smørgrav r = link(oldpath, newpath); 1353*bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 13544a421b63SDag-Erling Smørgrav send_status(id, status); 1355e4a9863fSDag-Erling Smørgrav free(oldpath); 1356e4a9863fSDag-Erling Smørgrav free(newpath); 13574a421b63SDag-Erling Smørgrav } 13584a421b63SDag-Erling Smørgrav 13594a421b63SDag-Erling Smørgrav static void 1360f7167e0eSDag-Erling Smørgrav process_extended_fsync(u_int32_t id) 13611e8db6e2SBrian Feldman { 1362*bc5531deSDag-Erling Smørgrav int handle, fd, r, status = SSH2_FX_OP_UNSUPPORTED; 13631e8db6e2SBrian Feldman 1364*bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 1365*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1366f7167e0eSDag-Erling Smørgrav debug3("request %u: fsync (handle %u)", id, handle); 1367f7167e0eSDag-Erling Smørgrav verbose("fsync \"%s\"", handle_to_name(handle)); 1368f7167e0eSDag-Erling Smørgrav if ((fd = handle_to_fd(handle)) < 0) 1369f7167e0eSDag-Erling Smørgrav status = SSH2_FX_NO_SUCH_FILE; 1370f7167e0eSDag-Erling Smørgrav else if (handle_is_ok(handle, HANDLE_FILE)) { 1371*bc5531deSDag-Erling Smørgrav r = fsync(fd); 1372*bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1373f7167e0eSDag-Erling Smørgrav } 1374f7167e0eSDag-Erling Smørgrav send_status(id, status); 1375f7167e0eSDag-Erling Smørgrav } 1376f7167e0eSDag-Erling Smørgrav 1377f7167e0eSDag-Erling Smørgrav static void 1378f7167e0eSDag-Erling Smørgrav process_extended(u_int32_t id) 1379f7167e0eSDag-Erling Smørgrav { 1380f7167e0eSDag-Erling Smørgrav char *request; 1381*bc5531deSDag-Erling Smørgrav int i, r; 1382f7167e0eSDag-Erling Smørgrav 1383*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &request, NULL)) != 0) 1384*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1385f7167e0eSDag-Erling Smørgrav for (i = 0; extended_handlers[i].handler != NULL; i++) { 1386f7167e0eSDag-Erling Smørgrav if (strcmp(request, extended_handlers[i].ext_name) == 0) { 1387f7167e0eSDag-Erling Smørgrav if (!request_permitted(&extended_handlers[i])) 1388f7167e0eSDag-Erling Smørgrav send_status(id, SSH2_FX_PERMISSION_DENIED); 1389d4af9e69SDag-Erling Smørgrav else 1390f7167e0eSDag-Erling Smørgrav extended_handlers[i].handler(id); 1391f7167e0eSDag-Erling Smørgrav break; 1392f7167e0eSDag-Erling Smørgrav } 1393f7167e0eSDag-Erling Smørgrav } 1394f7167e0eSDag-Erling Smørgrav if (extended_handlers[i].handler == NULL) { 1395f7167e0eSDag-Erling Smørgrav error("Unknown extended request \"%.100s\"", request); 13961e8db6e2SBrian Feldman send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 1397f7167e0eSDag-Erling Smørgrav } 1398e4a9863fSDag-Erling Smørgrav free(request); 13991e8db6e2SBrian Feldman } 1400b66f2d16SKris Kennaway 1401b66f2d16SKris Kennaway /* stolen from ssh-agent */ 1402b66f2d16SKris Kennaway 1403ae1f160dSDag-Erling Smørgrav static void 1404b66f2d16SKris Kennaway process(void) 1405b66f2d16SKris Kennaway { 1406*bc5531deSDag-Erling Smørgrav u_int msg_len; 1407*bc5531deSDag-Erling Smørgrav u_int buf_len; 1408*bc5531deSDag-Erling Smørgrav u_int consumed; 1409*bc5531deSDag-Erling Smørgrav u_char type; 1410*bc5531deSDag-Erling Smørgrav const u_char *cp; 1411*bc5531deSDag-Erling Smørgrav int i, r; 1412f7167e0eSDag-Erling Smørgrav u_int32_t id; 1413b66f2d16SKris Kennaway 1414*bc5531deSDag-Erling Smørgrav buf_len = sshbuf_len(iqueue); 1415545d5ecaSDag-Erling Smørgrav if (buf_len < 5) 1416b66f2d16SKris Kennaway return; /* Incomplete message. */ 1417*bc5531deSDag-Erling Smørgrav cp = sshbuf_ptr(iqueue); 1418761efaa7SDag-Erling Smørgrav msg_len = get_u32(cp); 1419021d409fSDag-Erling Smørgrav if (msg_len > SFTP_MAX_MSG_LENGTH) { 1420761efaa7SDag-Erling Smørgrav error("bad message from %s local user %s", 1421761efaa7SDag-Erling Smørgrav client_addr, pw->pw_name); 1422d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(11); 1423b66f2d16SKris Kennaway } 1424545d5ecaSDag-Erling Smørgrav if (buf_len < msg_len + 4) 1425b66f2d16SKris Kennaway return; 1426*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_consume(iqueue, 4)) != 0) 1427*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1428545d5ecaSDag-Erling Smørgrav buf_len -= 4; 1429*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u8(iqueue, &type)) != 0) 1430*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1431f7167e0eSDag-Erling Smørgrav 1432b66f2d16SKris Kennaway switch (type) { 14331e8db6e2SBrian Feldman case SSH2_FXP_INIT: 1434b66f2d16SKris Kennaway process_init(); 1435f7167e0eSDag-Erling Smørgrav init_done = 1; 14361e8db6e2SBrian Feldman break; 14371e8db6e2SBrian Feldman case SSH2_FXP_EXTENDED: 1438f7167e0eSDag-Erling Smørgrav if (!init_done) 1439f7167e0eSDag-Erling Smørgrav fatal("Received extended request before init"); 1440*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(iqueue, &id)) != 0) 1441*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1442f7167e0eSDag-Erling Smørgrav process_extended(id); 14431e8db6e2SBrian Feldman break; 1444b66f2d16SKris Kennaway default: 1445f7167e0eSDag-Erling Smørgrav if (!init_done) 1446f7167e0eSDag-Erling Smørgrav fatal("Received %u request before init", type); 1447*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(iqueue, &id)) != 0) 1448*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1449f7167e0eSDag-Erling Smørgrav for (i = 0; handlers[i].handler != NULL; i++) { 1450f7167e0eSDag-Erling Smørgrav if (type == handlers[i].type) { 1451f7167e0eSDag-Erling Smørgrav if (!request_permitted(&handlers[i])) { 1452f7167e0eSDag-Erling Smørgrav send_status(id, 1453f7167e0eSDag-Erling Smørgrav SSH2_FX_PERMISSION_DENIED); 1454f7167e0eSDag-Erling Smørgrav } else { 1455f7167e0eSDag-Erling Smørgrav handlers[i].handler(id); 1456f7167e0eSDag-Erling Smørgrav } 1457b66f2d16SKris Kennaway break; 1458b66f2d16SKris Kennaway } 1459f7167e0eSDag-Erling Smørgrav } 1460f7167e0eSDag-Erling Smørgrav if (handlers[i].handler == NULL) 1461f7167e0eSDag-Erling Smørgrav error("Unknown message %u", type); 1462f7167e0eSDag-Erling Smørgrav } 1463545d5ecaSDag-Erling Smørgrav /* discard the remaining bytes from the current packet */ 1464*bc5531deSDag-Erling Smørgrav if (buf_len < sshbuf_len(iqueue)) { 1465d4af9e69SDag-Erling Smørgrav error("iqueue grew unexpectedly"); 1466d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1467d4af9e69SDag-Erling Smørgrav } 1468*bc5531deSDag-Erling Smørgrav consumed = buf_len - sshbuf_len(iqueue); 1469d4af9e69SDag-Erling Smørgrav if (msg_len < consumed) { 1470f7167e0eSDag-Erling Smørgrav error("msg_len %u < consumed %u", msg_len, consumed); 1471d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1472d4af9e69SDag-Erling Smørgrav } 1473*bc5531deSDag-Erling Smørgrav if (msg_len > consumed && 1474*bc5531deSDag-Erling Smørgrav (r = sshbuf_consume(iqueue, msg_len - consumed)) != 0) 1475*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", __func__, ssh_err(r)); 1476b66f2d16SKris Kennaway } 1477b66f2d16SKris Kennaway 1478761efaa7SDag-Erling Smørgrav /* Cleanup handler that logs active handles upon normal exit */ 1479761efaa7SDag-Erling Smørgrav void 1480d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(int i) 1481761efaa7SDag-Erling Smørgrav { 1482761efaa7SDag-Erling Smørgrav if (pw != NULL && client_addr != NULL) { 1483761efaa7SDag-Erling Smørgrav handle_log_exit(); 1484761efaa7SDag-Erling Smørgrav logit("session closed for local user %s from [%s]", 1485761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr); 1486761efaa7SDag-Erling Smørgrav } 1487761efaa7SDag-Erling Smørgrav _exit(i); 1488761efaa7SDag-Erling Smørgrav } 1489761efaa7SDag-Erling Smørgrav 1490761efaa7SDag-Erling Smørgrav static void 1491d4af9e69SDag-Erling Smørgrav sftp_server_usage(void) 1492761efaa7SDag-Erling Smørgrav { 1493761efaa7SDag-Erling Smørgrav extern char *__progname; 1494761efaa7SDag-Erling Smørgrav 1495761efaa7SDag-Erling Smørgrav fprintf(stderr, 14966888a9beSDag-Erling Smørgrav "usage: %s [-ehR] [-d start_directory] [-f log_facility] " 1497f7167e0eSDag-Erling Smørgrav "[-l log_level]\n\t[-P blacklisted_requests] " 1498f7167e0eSDag-Erling Smørgrav "[-p whitelisted_requests] [-u umask]\n" 1499f7167e0eSDag-Erling Smørgrav " %s -Q protocol_feature\n", 1500f7167e0eSDag-Erling Smørgrav __progname, __progname); 1501761efaa7SDag-Erling Smørgrav exit(1); 1502761efaa7SDag-Erling Smørgrav } 1503761efaa7SDag-Erling Smørgrav 1504b66f2d16SKris Kennaway int 1505d4af9e69SDag-Erling Smørgrav sftp_server_main(int argc, char **argv, struct passwd *user_pw) 1506b66f2d16SKris Kennaway { 15071e8db6e2SBrian Feldman fd_set *rset, *wset; 1508*bc5531deSDag-Erling Smørgrav int i, r, in, out, max, ch, skipargs = 0, log_stderr = 0; 15091e8db6e2SBrian Feldman ssize_t len, olen, set_size; 1510761efaa7SDag-Erling Smørgrav SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; 15116888a9beSDag-Erling Smørgrav char *cp, *homedir = NULL, buf[4*4096]; 15124a421b63SDag-Erling Smørgrav long mask; 1513761efaa7SDag-Erling Smørgrav 1514761efaa7SDag-Erling Smørgrav extern char *optarg; 1515761efaa7SDag-Erling Smørgrav extern char *__progname; 15161e8db6e2SBrian Feldman 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 1602761efaa7SDag-Erling Smørgrav if ((cp = getenv("SSH_CONNECTION")) != NULL) { 1603761efaa7SDag-Erling Smørgrav client_addr = xstrdup(cp); 1604d4af9e69SDag-Erling Smørgrav if ((cp = strchr(client_addr, ' ')) == NULL) { 1605d4af9e69SDag-Erling Smørgrav error("Malformed SSH_CONNECTION variable: \"%s\"", 1606761efaa7SDag-Erling Smørgrav getenv("SSH_CONNECTION")); 1607d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1608d4af9e69SDag-Erling Smørgrav } 1609761efaa7SDag-Erling Smørgrav *cp = '\0'; 1610761efaa7SDag-Erling Smørgrav } else 1611761efaa7SDag-Erling Smørgrav client_addr = xstrdup("UNKNOWN"); 1612761efaa7SDag-Erling Smørgrav 1613761efaa7SDag-Erling Smørgrav logit("session opened for local user %s from [%s]", 1614761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr); 1615761efaa7SDag-Erling Smørgrav 1616b15c8340SDag-Erling Smørgrav in = STDIN_FILENO; 1617b15c8340SDag-Erling Smørgrav out = STDOUT_FILENO; 1618b66f2d16SKris Kennaway 161983d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN 162083d2307dSDag-Erling Smørgrav setmode(in, O_BINARY); 162183d2307dSDag-Erling Smørgrav setmode(out, O_BINARY); 162283d2307dSDag-Erling Smørgrav #endif 162383d2307dSDag-Erling Smørgrav 1624b66f2d16SKris Kennaway max = 0; 1625b66f2d16SKris Kennaway if (in > max) 1626b66f2d16SKris Kennaway max = in; 1627b66f2d16SKris Kennaway if (out > max) 1628b66f2d16SKris Kennaway max = out; 1629b66f2d16SKris Kennaway 1630*bc5531deSDag-Erling Smørgrav if ((iqueue = sshbuf_new()) == NULL) 1631*bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 1632*bc5531deSDag-Erling Smørgrav if ((oqueue = sshbuf_new()) == NULL) 1633*bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_new failed", __func__); 1634b66f2d16SKris Kennaway 16351e8db6e2SBrian Feldman set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 16361e8db6e2SBrian Feldman rset = (fd_set *)xmalloc(set_size); 16371e8db6e2SBrian Feldman wset = (fd_set *)xmalloc(set_size); 1638b66f2d16SKris Kennaway 16396888a9beSDag-Erling Smørgrav if (homedir != NULL) { 16406888a9beSDag-Erling Smørgrav if (chdir(homedir) != 0) { 16416888a9beSDag-Erling Smørgrav error("chdir to \"%s\" failed: %s", homedir, 16426888a9beSDag-Erling Smørgrav strerror(errno)); 16436888a9beSDag-Erling Smørgrav } 16446888a9beSDag-Erling Smørgrav } 16456888a9beSDag-Erling Smørgrav 16461e8db6e2SBrian Feldman for (;;) { 16471e8db6e2SBrian Feldman memset(rset, 0, set_size); 16481e8db6e2SBrian Feldman memset(wset, 0, set_size); 16491e8db6e2SBrian Feldman 1650d4af9e69SDag-Erling Smørgrav /* 1651d4af9e69SDag-Erling Smørgrav * Ensure that we can read a full buffer and handle 1652d4af9e69SDag-Erling Smørgrav * the worst-case length packet it can generate, 1653d4af9e69SDag-Erling Smørgrav * otherwise apply backpressure by stopping reads. 1654d4af9e69SDag-Erling Smørgrav */ 1655*bc5531deSDag-Erling Smørgrav if ((r = sshbuf_check_reserve(iqueue, sizeof(buf))) == 0 && 1656*bc5531deSDag-Erling Smørgrav (r = sshbuf_check_reserve(oqueue, 1657*bc5531deSDag-Erling Smørgrav SFTP_MAX_MSG_LENGTH)) == 0) 16581e8db6e2SBrian Feldman FD_SET(in, rset); 1659*bc5531deSDag-Erling Smørgrav else if (r != SSH_ERR_NO_BUFFER_SPACE) 1660*bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_check_reserve failed: %s", 1661*bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1662d4af9e69SDag-Erling Smørgrav 1663*bc5531deSDag-Erling Smørgrav olen = sshbuf_len(oqueue); 1664b66f2d16SKris Kennaway if (olen > 0) 16651e8db6e2SBrian Feldman FD_SET(out, wset); 1666b66f2d16SKris Kennaway 16671e8db6e2SBrian Feldman if (select(max+1, rset, wset, NULL, NULL) < 0) { 1668b66f2d16SKris Kennaway if (errno == EINTR) 1669b66f2d16SKris Kennaway continue; 1670761efaa7SDag-Erling Smørgrav error("select: %s", strerror(errno)); 1671d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(2); 1672b66f2d16SKris Kennaway } 1673b66f2d16SKris Kennaway 1674b66f2d16SKris Kennaway /* copy stdin to iqueue */ 16751e8db6e2SBrian Feldman if (FD_ISSET(in, rset)) { 1676b66f2d16SKris Kennaway len = read(in, buf, sizeof buf); 1677b66f2d16SKris Kennaway if (len == 0) { 1678b66f2d16SKris Kennaway debug("read eof"); 1679d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(0); 1680b66f2d16SKris Kennaway } else if (len < 0) { 1681761efaa7SDag-Erling Smørgrav error("read: %s", strerror(errno)); 1682d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1); 1683*bc5531deSDag-Erling Smørgrav } else if ((r = sshbuf_put(iqueue, buf, len)) != 0) { 1684*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", 1685*bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1686b66f2d16SKris Kennaway } 1687b66f2d16SKris Kennaway } 1688b66f2d16SKris Kennaway /* send oqueue to stdout */ 16891e8db6e2SBrian Feldman if (FD_ISSET(out, wset)) { 1690*bc5531deSDag-Erling Smørgrav len = write(out, sshbuf_ptr(oqueue), olen); 1691b66f2d16SKris Kennaway if (len < 0) { 1692761efaa7SDag-Erling Smørgrav error("write: %s", strerror(errno)); 1693d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1); 1694*bc5531deSDag-Erling Smørgrav } else if ((r = sshbuf_consume(oqueue, len)) != 0) { 1695*bc5531deSDag-Erling Smørgrav fatal("%s: buffer error: %s", 1696*bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1697b66f2d16SKris Kennaway } 1698b66f2d16SKris Kennaway } 1699d4af9e69SDag-Erling Smørgrav 1700d4af9e69SDag-Erling Smørgrav /* 1701d4af9e69SDag-Erling Smørgrav * Process requests from client if we can fit the results 1702d4af9e69SDag-Erling Smørgrav * into the output buffer, otherwise stop processing input 1703d4af9e69SDag-Erling Smørgrav * and let the output queue drain. 1704d4af9e69SDag-Erling Smørgrav */ 1705*bc5531deSDag-Erling Smørgrav r = sshbuf_check_reserve(oqueue, SFTP_MAX_MSG_LENGTH); 1706*bc5531deSDag-Erling Smørgrav if (r == 0) 1707b66f2d16SKris Kennaway process(); 1708*bc5531deSDag-Erling Smørgrav else if (r != SSH_ERR_NO_BUFFER_SPACE) 1709*bc5531deSDag-Erling Smørgrav fatal("%s: sshbuf_check_reserve: %s", 1710*bc5531deSDag-Erling Smørgrav __func__, ssh_err(r)); 1711b66f2d16SKris Kennaway } 1712b66f2d16SKris Kennaway } 1713