1*4d3fc8b0SEd Maste /* $OpenBSD: sftp-server.c,v 1.146 2023/03/07 05:37:26 djm Exp $ */ 2b66f2d16SKris Kennaway /* 3efcad6b7SDag-Erling Smørgrav * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. 4b66f2d16SKris Kennaway * 5efcad6b7SDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any 6efcad6b7SDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above 7efcad6b7SDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies. 8b66f2d16SKris Kennaway * 9efcad6b7SDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10efcad6b7SDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11efcad6b7SDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12efcad6b7SDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13efcad6b7SDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14efcad6b7SDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15efcad6b7SDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16b66f2d16SKris Kennaway */ 17b66f2d16SKris Kennaway 18761efaa7SDag-Erling Smørgrav #include "includes.h" 19761efaa7SDag-Erling Smørgrav 20761efaa7SDag-Erling Smørgrav #include <sys/types.h> 21761efaa7SDag-Erling Smørgrav #include <sys/stat.h> 2219261079SEd Maste #include <sys/resource.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 32761efaa7SDag-Erling Smørgrav 33761efaa7SDag-Erling Smørgrav #include <dirent.h> 34761efaa7SDag-Erling Smørgrav #include <errno.h> 35761efaa7SDag-Erling Smørgrav #include <fcntl.h> 361323ec57SEd Maste #ifdef HAVE_POLL_H 371323ec57SEd Maste #include <poll.h> 381323ec57SEd Maste #endif 39761efaa7SDag-Erling Smørgrav #include <pwd.h> 4038a52bd3SEd Maste #include <grp.h> 41761efaa7SDag-Erling Smørgrav #include <stdlib.h> 42761efaa7SDag-Erling Smørgrav #include <stdio.h> 43761efaa7SDag-Erling Smørgrav #include <string.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 4887c1498dSEd Maste #include "atomicio.h" 49b66f2d16SKris Kennaway #include "xmalloc.h" 50bc5531deSDag-Erling Smørgrav #include "sshbuf.h" 51bc5531deSDag-Erling Smørgrav #include "ssherr.h" 52761efaa7SDag-Erling Smørgrav #include "log.h" 53021d409fSDag-Erling Smørgrav #include "misc.h" 54f7167e0eSDag-Erling Smørgrav #include "match.h" 55761efaa7SDag-Erling Smørgrav #include "uidswap.h" 56b66f2d16SKris Kennaway 571e8db6e2SBrian Feldman #include "sftp.h" 581e8db6e2SBrian Feldman #include "sftp-common.h" 59b66f2d16SKris Kennaway 6019261079SEd Maste char *sftp_realpath(const char *, char *); /* sftp-realpath.c */ 6119261079SEd Maste 6219261079SEd Maste /* Maximum data read that we are willing to accept */ 6319261079SEd Maste #define SFTP_MAX_READ_LENGTH (SFTP_MAX_MSG_LENGTH - 1024) 6419261079SEd Maste 65761efaa7SDag-Erling Smørgrav /* Our verbosity */ 66f7167e0eSDag-Erling Smørgrav static LogLevel log_level = SYSLOG_LEVEL_ERROR; 67761efaa7SDag-Erling Smørgrav 68761efaa7SDag-Erling Smørgrav /* Our client */ 69f7167e0eSDag-Erling Smørgrav static struct passwd *pw = NULL; 70f7167e0eSDag-Erling Smørgrav static char *client_addr = NULL; 7183d2307dSDag-Erling Smørgrav 72b66f2d16SKris Kennaway /* input and output queue */ 73bc5531deSDag-Erling Smørgrav struct sshbuf *iqueue; 74bc5531deSDag-Erling Smørgrav struct sshbuf *oqueue; 75b66f2d16SKris Kennaway 761e8db6e2SBrian Feldman /* Version of client */ 77f7167e0eSDag-Erling Smørgrav static u_int version; 78f7167e0eSDag-Erling Smørgrav 79f7167e0eSDag-Erling Smørgrav /* SSH2_FXP_INIT received */ 80f7167e0eSDag-Erling Smørgrav static int init_done; 811e8db6e2SBrian Feldman 82b15c8340SDag-Erling Smørgrav /* Disable writes */ 83f7167e0eSDag-Erling Smørgrav static int readonly; 84f7167e0eSDag-Erling Smørgrav 85f7167e0eSDag-Erling Smørgrav /* Requests that are allowed/denied */ 8619261079SEd Maste static char *request_allowlist, *request_denylist; 87b15c8340SDag-Erling Smørgrav 88d95e11bfSDag-Erling Smørgrav /* portable attributes, etc. */ 89b66f2d16SKris Kennaway typedef struct Stat Stat; 90b66f2d16SKris Kennaway 911e8db6e2SBrian Feldman struct Stat { 92b66f2d16SKris Kennaway char *name; 93b66f2d16SKris Kennaway char *long_name; 94b66f2d16SKris Kennaway Attrib attrib; 95b66f2d16SKris Kennaway }; 96b66f2d16SKris Kennaway 97f7167e0eSDag-Erling Smørgrav /* Packet handlers */ 98f7167e0eSDag-Erling Smørgrav static void process_open(u_int32_t id); 99f7167e0eSDag-Erling Smørgrav static void process_close(u_int32_t id); 100f7167e0eSDag-Erling Smørgrav static void process_read(u_int32_t id); 101f7167e0eSDag-Erling Smørgrav static void process_write(u_int32_t id); 102f7167e0eSDag-Erling Smørgrav static void process_stat(u_int32_t id); 103f7167e0eSDag-Erling Smørgrav static void process_lstat(u_int32_t id); 104f7167e0eSDag-Erling Smørgrav static void process_fstat(u_int32_t id); 105f7167e0eSDag-Erling Smørgrav static void process_setstat(u_int32_t id); 106f7167e0eSDag-Erling Smørgrav static void process_fsetstat(u_int32_t id); 107f7167e0eSDag-Erling Smørgrav static void process_opendir(u_int32_t id); 108f7167e0eSDag-Erling Smørgrav static void process_readdir(u_int32_t id); 109f7167e0eSDag-Erling Smørgrav static void process_remove(u_int32_t id); 110f7167e0eSDag-Erling Smørgrav static void process_mkdir(u_int32_t id); 111f7167e0eSDag-Erling Smørgrav static void process_rmdir(u_int32_t id); 112f7167e0eSDag-Erling Smørgrav static void process_realpath(u_int32_t id); 113f7167e0eSDag-Erling Smørgrav static void process_rename(u_int32_t id); 114f7167e0eSDag-Erling Smørgrav static void process_readlink(u_int32_t id); 115f7167e0eSDag-Erling Smørgrav static void process_symlink(u_int32_t id); 116f7167e0eSDag-Erling Smørgrav static void process_extended_posix_rename(u_int32_t id); 117f7167e0eSDag-Erling Smørgrav static void process_extended_statvfs(u_int32_t id); 118f7167e0eSDag-Erling Smørgrav static void process_extended_fstatvfs(u_int32_t id); 119f7167e0eSDag-Erling Smørgrav static void process_extended_hardlink(u_int32_t id); 120f7167e0eSDag-Erling Smørgrav static void process_extended_fsync(u_int32_t id); 12119261079SEd Maste static void process_extended_lsetstat(u_int32_t id); 12219261079SEd Maste static void process_extended_limits(u_int32_t id); 12319261079SEd Maste static void process_extended_expand(u_int32_t id); 12487c1498dSEd Maste static void process_extended_copy_data(u_int32_t id); 12538a52bd3SEd Maste static void process_extended_home_directory(u_int32_t id); 12638a52bd3SEd Maste static void process_extended_get_users_groups_by_id(u_int32_t id); 127f7167e0eSDag-Erling Smørgrav static void process_extended(u_int32_t id); 128f7167e0eSDag-Erling Smørgrav 129f7167e0eSDag-Erling Smørgrav struct sftp_handler { 130f7167e0eSDag-Erling Smørgrav const char *name; /* user-visible name for fine-grained perms */ 131f7167e0eSDag-Erling Smørgrav const char *ext_name; /* extended request name */ 132f7167e0eSDag-Erling Smørgrav u_int type; /* packet type, for non extended packets */ 133f7167e0eSDag-Erling Smørgrav void (*handler)(u_int32_t); 134f7167e0eSDag-Erling Smørgrav int does_write; /* if nonzero, banned for readonly mode */ 135f7167e0eSDag-Erling Smørgrav }; 136f7167e0eSDag-Erling Smørgrav 13719261079SEd Maste static const struct sftp_handler handlers[] = { 138f7167e0eSDag-Erling Smørgrav /* NB. SSH2_FXP_OPEN does the readonly check in the handler itself */ 139f7167e0eSDag-Erling Smørgrav { "open", NULL, SSH2_FXP_OPEN, process_open, 0 }, 140f7167e0eSDag-Erling Smørgrav { "close", NULL, SSH2_FXP_CLOSE, process_close, 0 }, 141f7167e0eSDag-Erling Smørgrav { "read", NULL, SSH2_FXP_READ, process_read, 0 }, 142f7167e0eSDag-Erling Smørgrav { "write", NULL, SSH2_FXP_WRITE, process_write, 1 }, 143f7167e0eSDag-Erling Smørgrav { "lstat", NULL, SSH2_FXP_LSTAT, process_lstat, 0 }, 144f7167e0eSDag-Erling Smørgrav { "fstat", NULL, SSH2_FXP_FSTAT, process_fstat, 0 }, 145f7167e0eSDag-Erling Smørgrav { "setstat", NULL, SSH2_FXP_SETSTAT, process_setstat, 1 }, 146f7167e0eSDag-Erling Smørgrav { "fsetstat", NULL, SSH2_FXP_FSETSTAT, process_fsetstat, 1 }, 147f7167e0eSDag-Erling Smørgrav { "opendir", NULL, SSH2_FXP_OPENDIR, process_opendir, 0 }, 148f7167e0eSDag-Erling Smørgrav { "readdir", NULL, SSH2_FXP_READDIR, process_readdir, 0 }, 149f7167e0eSDag-Erling Smørgrav { "remove", NULL, SSH2_FXP_REMOVE, process_remove, 1 }, 150f7167e0eSDag-Erling Smørgrav { "mkdir", NULL, SSH2_FXP_MKDIR, process_mkdir, 1 }, 151f7167e0eSDag-Erling Smørgrav { "rmdir", NULL, SSH2_FXP_RMDIR, process_rmdir, 1 }, 152f7167e0eSDag-Erling Smørgrav { "realpath", NULL, SSH2_FXP_REALPATH, process_realpath, 0 }, 153f7167e0eSDag-Erling Smørgrav { "stat", NULL, SSH2_FXP_STAT, process_stat, 0 }, 154f7167e0eSDag-Erling Smørgrav { "rename", NULL, SSH2_FXP_RENAME, process_rename, 1 }, 155f7167e0eSDag-Erling Smørgrav { "readlink", NULL, SSH2_FXP_READLINK, process_readlink, 0 }, 156f7167e0eSDag-Erling Smørgrav { "symlink", NULL, SSH2_FXP_SYMLINK, process_symlink, 1 }, 157f7167e0eSDag-Erling Smørgrav { NULL, NULL, 0, NULL, 0 } 158f7167e0eSDag-Erling Smørgrav }; 159f7167e0eSDag-Erling Smørgrav 160f7167e0eSDag-Erling Smørgrav /* SSH2_FXP_EXTENDED submessages */ 16119261079SEd Maste static const struct sftp_handler extended_handlers[] = { 162f7167e0eSDag-Erling Smørgrav { "posix-rename", "posix-rename@openssh.com", 0, 163f7167e0eSDag-Erling Smørgrav process_extended_posix_rename, 1 }, 164f7167e0eSDag-Erling Smørgrav { "statvfs", "statvfs@openssh.com", 0, process_extended_statvfs, 0 }, 165f7167e0eSDag-Erling Smørgrav { "fstatvfs", "fstatvfs@openssh.com", 0, process_extended_fstatvfs, 0 }, 166f7167e0eSDag-Erling Smørgrav { "hardlink", "hardlink@openssh.com", 0, process_extended_hardlink, 1 }, 167f7167e0eSDag-Erling Smørgrav { "fsync", "fsync@openssh.com", 0, process_extended_fsync, 1 }, 16819261079SEd Maste { "lsetstat", "lsetstat@openssh.com", 0, process_extended_lsetstat, 1 }, 16919261079SEd Maste { "limits", "limits@openssh.com", 0, process_extended_limits, 0 }, 17019261079SEd Maste { "expand-path", "expand-path@openssh.com", 0, 17119261079SEd Maste process_extended_expand, 0 }, 17287c1498dSEd Maste { "copy-data", "copy-data", 0, process_extended_copy_data, 1 }, 17338a52bd3SEd Maste { "home-directory", "home-directory", 0, 17438a52bd3SEd Maste process_extended_home_directory, 0 }, 17538a52bd3SEd Maste { "users-groups-by-id", "users-groups-by-id@openssh.com", 0, 17638a52bd3SEd Maste process_extended_get_users_groups_by_id, 0 }, 177f7167e0eSDag-Erling Smørgrav { NULL, NULL, 0, NULL, 0 } 178f7167e0eSDag-Erling Smørgrav }; 179f7167e0eSDag-Erling Smørgrav 18019261079SEd Maste static const struct sftp_handler * 18119261079SEd Maste extended_handler_byname(const char *name) 18219261079SEd Maste { 18319261079SEd Maste int i; 18419261079SEd Maste 18519261079SEd Maste for (i = 0; extended_handlers[i].handler != NULL; i++) { 18619261079SEd Maste if (strcmp(name, extended_handlers[i].ext_name) == 0) 18719261079SEd Maste return &extended_handlers[i]; 18819261079SEd Maste } 18919261079SEd Maste return NULL; 19019261079SEd Maste } 19119261079SEd Maste 192f7167e0eSDag-Erling Smørgrav static int 19319261079SEd Maste request_permitted(const struct sftp_handler *h) 194f7167e0eSDag-Erling Smørgrav { 195f7167e0eSDag-Erling Smørgrav char *result; 196f7167e0eSDag-Erling Smørgrav 197f7167e0eSDag-Erling Smørgrav if (readonly && h->does_write) { 198f7167e0eSDag-Erling Smørgrav verbose("Refusing %s request in read-only mode", h->name); 199f7167e0eSDag-Erling Smørgrav return 0; 200f7167e0eSDag-Erling Smørgrav } 20119261079SEd Maste if (request_denylist != NULL && 20219261079SEd Maste ((result = match_list(h->name, request_denylist, NULL))) != NULL) { 203f7167e0eSDag-Erling Smørgrav free(result); 20419261079SEd Maste verbose("Refusing denylisted %s request", h->name); 205f7167e0eSDag-Erling Smørgrav return 0; 206f7167e0eSDag-Erling Smørgrav } 20719261079SEd Maste if (request_allowlist != NULL && 20819261079SEd Maste ((result = match_list(h->name, request_allowlist, NULL))) != NULL) { 209f7167e0eSDag-Erling Smørgrav free(result); 21019261079SEd Maste debug2("Permitting allowlisted %s request", h->name); 211f7167e0eSDag-Erling Smørgrav return 1; 212f7167e0eSDag-Erling Smørgrav } 21319261079SEd Maste if (request_allowlist != NULL) { 21419261079SEd Maste verbose("Refusing non-allowlisted %s request", h->name); 215f7167e0eSDag-Erling Smørgrav return 0; 216f7167e0eSDag-Erling Smørgrav } 217f7167e0eSDag-Erling Smørgrav return 1; 218f7167e0eSDag-Erling Smørgrav } 219f7167e0eSDag-Erling Smørgrav 220ae1f160dSDag-Erling Smørgrav static int 221b66f2d16SKris Kennaway errno_to_portable(int unixerrno) 222b66f2d16SKris Kennaway { 223b66f2d16SKris Kennaway int ret = 0; 2241e8db6e2SBrian Feldman 225b66f2d16SKris Kennaway switch (unixerrno) { 226b66f2d16SKris Kennaway case 0: 2271e8db6e2SBrian Feldman ret = SSH2_FX_OK; 228b66f2d16SKris Kennaway break; 229b66f2d16SKris Kennaway case ENOENT: 230b66f2d16SKris Kennaway case ENOTDIR: 231b66f2d16SKris Kennaway case EBADF: 232b66f2d16SKris Kennaway case ELOOP: 2331e8db6e2SBrian Feldman ret = SSH2_FX_NO_SUCH_FILE; 234b66f2d16SKris Kennaway break; 235b66f2d16SKris Kennaway case EPERM: 236b66f2d16SKris Kennaway case EACCES: 237b66f2d16SKris Kennaway case EFAULT: 2381e8db6e2SBrian Feldman ret = SSH2_FX_PERMISSION_DENIED; 239b66f2d16SKris Kennaway break; 240b66f2d16SKris Kennaway case ENAMETOOLONG: 241b66f2d16SKris Kennaway case EINVAL: 2421e8db6e2SBrian Feldman ret = SSH2_FX_BAD_MESSAGE; 243b66f2d16SKris Kennaway break; 244d4af9e69SDag-Erling Smørgrav case ENOSYS: 245d4af9e69SDag-Erling Smørgrav ret = SSH2_FX_OP_UNSUPPORTED; 246d4af9e69SDag-Erling Smørgrav break; 247b66f2d16SKris Kennaway default: 2481e8db6e2SBrian Feldman ret = SSH2_FX_FAILURE; 249b66f2d16SKris Kennaway break; 250b66f2d16SKris Kennaway } 251b66f2d16SKris Kennaway return ret; 252b66f2d16SKris Kennaway } 253b66f2d16SKris Kennaway 254ae1f160dSDag-Erling Smørgrav static int 255b66f2d16SKris Kennaway flags_from_portable(int pflags) 256b66f2d16SKris Kennaway { 257b66f2d16SKris Kennaway int flags = 0; 2581e8db6e2SBrian Feldman 2591e8db6e2SBrian Feldman if ((pflags & SSH2_FXF_READ) && 2601e8db6e2SBrian Feldman (pflags & SSH2_FXF_WRITE)) { 261b66f2d16SKris Kennaway flags = O_RDWR; 2621e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_READ) { 263b66f2d16SKris Kennaway flags = O_RDONLY; 2641e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_WRITE) { 265b66f2d16SKris Kennaway flags = O_WRONLY; 266b66f2d16SKris Kennaway } 267f7167e0eSDag-Erling Smørgrav if (pflags & SSH2_FXF_APPEND) 268f7167e0eSDag-Erling Smørgrav flags |= O_APPEND; 2691e8db6e2SBrian Feldman if (pflags & SSH2_FXF_CREAT) 270b66f2d16SKris Kennaway flags |= O_CREAT; 2711e8db6e2SBrian Feldman if (pflags & SSH2_FXF_TRUNC) 272b66f2d16SKris Kennaway flags |= O_TRUNC; 2731e8db6e2SBrian Feldman if (pflags & SSH2_FXF_EXCL) 274b66f2d16SKris Kennaway flags |= O_EXCL; 275b66f2d16SKris Kennaway return flags; 276b66f2d16SKris Kennaway } 277b66f2d16SKris Kennaway 278761efaa7SDag-Erling Smørgrav static const char * 279761efaa7SDag-Erling Smørgrav string_from_portable(int pflags) 280761efaa7SDag-Erling Smørgrav { 281761efaa7SDag-Erling Smørgrav static char ret[128]; 282761efaa7SDag-Erling Smørgrav 283761efaa7SDag-Erling Smørgrav *ret = '\0'; 284761efaa7SDag-Erling Smørgrav 285761efaa7SDag-Erling Smørgrav #define PAPPEND(str) { \ 286761efaa7SDag-Erling Smørgrav if (*ret != '\0') \ 287761efaa7SDag-Erling Smørgrav strlcat(ret, ",", sizeof(ret)); \ 288761efaa7SDag-Erling Smørgrav strlcat(ret, str, sizeof(ret)); \ 289761efaa7SDag-Erling Smørgrav } 290761efaa7SDag-Erling Smørgrav 291761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_READ) 292761efaa7SDag-Erling Smørgrav PAPPEND("READ") 293761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_WRITE) 294761efaa7SDag-Erling Smørgrav PAPPEND("WRITE") 295f7167e0eSDag-Erling Smørgrav if (pflags & SSH2_FXF_APPEND) 296f7167e0eSDag-Erling Smørgrav PAPPEND("APPEND") 297761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_CREAT) 298761efaa7SDag-Erling Smørgrav PAPPEND("CREATE") 299761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_TRUNC) 300761efaa7SDag-Erling Smørgrav PAPPEND("TRUNCATE") 301761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_EXCL) 302761efaa7SDag-Erling Smørgrav PAPPEND("EXCL") 303761efaa7SDag-Erling Smørgrav 304761efaa7SDag-Erling Smørgrav return ret; 305761efaa7SDag-Erling Smørgrav } 306761efaa7SDag-Erling Smørgrav 307b66f2d16SKris Kennaway /* handle handles */ 308b66f2d16SKris Kennaway 309b66f2d16SKris Kennaway typedef struct Handle Handle; 310b66f2d16SKris Kennaway struct Handle { 311b66f2d16SKris Kennaway int use; 312b66f2d16SKris Kennaway DIR *dirp; 313b66f2d16SKris Kennaway int fd; 314f7167e0eSDag-Erling Smørgrav int flags; 315b66f2d16SKris Kennaway char *name; 316761efaa7SDag-Erling Smørgrav u_int64_t bytes_read, bytes_write; 317d4af9e69SDag-Erling Smørgrav int next_unused; 318b66f2d16SKris Kennaway }; 3191e8db6e2SBrian Feldman 320b66f2d16SKris Kennaway enum { 321b66f2d16SKris Kennaway HANDLE_UNUSED, 322b66f2d16SKris Kennaway HANDLE_DIR, 323b66f2d16SKris Kennaway HANDLE_FILE 324b66f2d16SKris Kennaway }; 3251e8db6e2SBrian Feldman 32619261079SEd Maste static Handle *handles = NULL; 32719261079SEd Maste static u_int num_handles = 0; 32819261079SEd Maste static int first_unused_handle = -1; 329b66f2d16SKris Kennaway 330d4af9e69SDag-Erling Smørgrav static void handle_unused(int i) 331b66f2d16SKris Kennaway { 332b66f2d16SKris Kennaway handles[i].use = HANDLE_UNUSED; 333d4af9e69SDag-Erling Smørgrav handles[i].next_unused = first_unused_handle; 334d4af9e69SDag-Erling Smørgrav first_unused_handle = i; 335b66f2d16SKris Kennaway } 336b66f2d16SKris Kennaway 337ae1f160dSDag-Erling Smørgrav static int 338f7167e0eSDag-Erling Smørgrav handle_new(int use, const char *name, int fd, int flags, DIR *dirp) 339b66f2d16SKris Kennaway { 340d4af9e69SDag-Erling Smørgrav int i; 3411e8db6e2SBrian Feldman 342d4af9e69SDag-Erling Smørgrav if (first_unused_handle == -1) { 343d4af9e69SDag-Erling Smørgrav if (num_handles + 1 <= num_handles) 344d4af9e69SDag-Erling Smørgrav return -1; 345d4af9e69SDag-Erling Smørgrav num_handles++; 346557f75e5SDag-Erling Smørgrav handles = xreallocarray(handles, num_handles, sizeof(Handle)); 347d4af9e69SDag-Erling Smørgrav handle_unused(num_handles - 1); 348d4af9e69SDag-Erling Smørgrav } 349d4af9e69SDag-Erling Smørgrav 350d4af9e69SDag-Erling Smørgrav i = first_unused_handle; 351d4af9e69SDag-Erling Smørgrav first_unused_handle = handles[i].next_unused; 352d4af9e69SDag-Erling Smørgrav 353b66f2d16SKris Kennaway handles[i].use = use; 354b66f2d16SKris Kennaway handles[i].dirp = dirp; 355b66f2d16SKris Kennaway handles[i].fd = fd; 356f7167e0eSDag-Erling Smørgrav handles[i].flags = flags; 357d0c8c0bcSDag-Erling Smørgrav handles[i].name = xstrdup(name); 358761efaa7SDag-Erling Smørgrav handles[i].bytes_read = handles[i].bytes_write = 0; 359d4af9e69SDag-Erling Smørgrav 360b66f2d16SKris Kennaway return i; 361b66f2d16SKris Kennaway } 362b66f2d16SKris Kennaway 363ae1f160dSDag-Erling Smørgrav static int 364b66f2d16SKris Kennaway handle_is_ok(int i, int type) 365b66f2d16SKris Kennaway { 366d4af9e69SDag-Erling Smørgrav return i >= 0 && (u_int)i < num_handles && handles[i].use == type; 367b66f2d16SKris Kennaway } 368b66f2d16SKris Kennaway 369ae1f160dSDag-Erling Smørgrav static int 370bc5531deSDag-Erling Smørgrav handle_to_string(int handle, u_char **stringp, int *hlenp) 371b66f2d16SKris Kennaway { 372b66f2d16SKris Kennaway if (stringp == NULL || hlenp == NULL) 373b66f2d16SKris Kennaway return -1; 3741e8db6e2SBrian Feldman *stringp = xmalloc(sizeof(int32_t)); 375761efaa7SDag-Erling Smørgrav put_u32(*stringp, handle); 3761e8db6e2SBrian Feldman *hlenp = sizeof(int32_t); 377b66f2d16SKris Kennaway return 0; 378b66f2d16SKris Kennaway } 379b66f2d16SKris Kennaway 380ae1f160dSDag-Erling Smørgrav static int 381bc5531deSDag-Erling Smørgrav handle_from_string(const u_char *handle, u_int hlen) 382b66f2d16SKris Kennaway { 3831e8db6e2SBrian Feldman int val; 3841e8db6e2SBrian Feldman 3851e8db6e2SBrian Feldman if (hlen != sizeof(int32_t)) 386b66f2d16SKris Kennaway return -1; 387761efaa7SDag-Erling Smørgrav val = get_u32(handle); 388b66f2d16SKris Kennaway if (handle_is_ok(val, HANDLE_FILE) || 389b66f2d16SKris Kennaway handle_is_ok(val, HANDLE_DIR)) 390b66f2d16SKris Kennaway return val; 391b66f2d16SKris Kennaway return -1; 392b66f2d16SKris Kennaway } 393b66f2d16SKris Kennaway 394ae1f160dSDag-Erling Smørgrav static char * 395b66f2d16SKris Kennaway handle_to_name(int handle) 396b66f2d16SKris Kennaway { 397b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)|| 398b66f2d16SKris Kennaway handle_is_ok(handle, HANDLE_FILE)) 399b66f2d16SKris Kennaway return handles[handle].name; 400b66f2d16SKris Kennaway return NULL; 401b66f2d16SKris Kennaway } 402b66f2d16SKris Kennaway 403ae1f160dSDag-Erling Smørgrav static DIR * 404b66f2d16SKris Kennaway handle_to_dir(int handle) 405b66f2d16SKris Kennaway { 406b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)) 407b66f2d16SKris Kennaway return handles[handle].dirp; 408b66f2d16SKris Kennaway return NULL; 409b66f2d16SKris Kennaway } 410b66f2d16SKris Kennaway 411ae1f160dSDag-Erling Smørgrav static int 412b66f2d16SKris Kennaway handle_to_fd(int handle) 413b66f2d16SKris Kennaway { 414b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) 415b66f2d16SKris Kennaway return handles[handle].fd; 416b66f2d16SKris Kennaway return -1; 417b66f2d16SKris Kennaway } 418b66f2d16SKris Kennaway 419f7167e0eSDag-Erling Smørgrav static int 420f7167e0eSDag-Erling Smørgrav handle_to_flags(int handle) 421f7167e0eSDag-Erling Smørgrav { 422f7167e0eSDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 423f7167e0eSDag-Erling Smørgrav return handles[handle].flags; 424f7167e0eSDag-Erling Smørgrav return 0; 425f7167e0eSDag-Erling Smørgrav } 426f7167e0eSDag-Erling Smørgrav 427761efaa7SDag-Erling Smørgrav static void 428761efaa7SDag-Erling Smørgrav handle_update_read(int handle, ssize_t bytes) 429761efaa7SDag-Erling Smørgrav { 430761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 431761efaa7SDag-Erling Smørgrav handles[handle].bytes_read += bytes; 432761efaa7SDag-Erling Smørgrav } 433761efaa7SDag-Erling Smørgrav 434761efaa7SDag-Erling Smørgrav static void 435761efaa7SDag-Erling Smørgrav handle_update_write(int handle, ssize_t bytes) 436761efaa7SDag-Erling Smørgrav { 437761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 438761efaa7SDag-Erling Smørgrav handles[handle].bytes_write += bytes; 439761efaa7SDag-Erling Smørgrav } 440761efaa7SDag-Erling Smørgrav 441761efaa7SDag-Erling Smørgrav static u_int64_t 442761efaa7SDag-Erling Smørgrav handle_bytes_read(int handle) 443761efaa7SDag-Erling Smørgrav { 444761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 445761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_read); 446761efaa7SDag-Erling Smørgrav return 0; 447761efaa7SDag-Erling Smørgrav } 448761efaa7SDag-Erling Smørgrav 449761efaa7SDag-Erling Smørgrav static u_int64_t 450761efaa7SDag-Erling Smørgrav handle_bytes_write(int handle) 451761efaa7SDag-Erling Smørgrav { 452761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 453761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_write); 454761efaa7SDag-Erling Smørgrav return 0; 455761efaa7SDag-Erling Smørgrav } 456761efaa7SDag-Erling Smørgrav 457ae1f160dSDag-Erling Smørgrav static int 458b66f2d16SKris Kennaway handle_close(int handle) 459b66f2d16SKris Kennaway { 460b66f2d16SKris Kennaway int ret = -1; 4611e8db6e2SBrian Feldman 462b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) { 463b66f2d16SKris Kennaway ret = close(handles[handle].fd); 464e4a9863fSDag-Erling Smørgrav free(handles[handle].name); 465d4af9e69SDag-Erling Smørgrav handle_unused(handle); 466b66f2d16SKris Kennaway } else if (handle_is_ok(handle, HANDLE_DIR)) { 467b66f2d16SKris Kennaway ret = closedir(handles[handle].dirp); 468e4a9863fSDag-Erling Smørgrav free(handles[handle].name); 469d4af9e69SDag-Erling Smørgrav handle_unused(handle); 470b66f2d16SKris Kennaway } else { 471b66f2d16SKris Kennaway errno = ENOENT; 472b66f2d16SKris Kennaway } 473b66f2d16SKris Kennaway return ret; 474b66f2d16SKris Kennaway } 475b66f2d16SKris Kennaway 476761efaa7SDag-Erling Smørgrav static void 477761efaa7SDag-Erling Smørgrav handle_log_close(int handle, char *emsg) 478761efaa7SDag-Erling Smørgrav { 479761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) { 480761efaa7SDag-Erling Smørgrav logit("%s%sclose \"%s\" bytes read %llu written %llu", 481761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 482761efaa7SDag-Erling Smørgrav handle_to_name(handle), 483d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_read(handle), 484d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_write(handle)); 485761efaa7SDag-Erling Smørgrav } else { 486761efaa7SDag-Erling Smørgrav logit("%s%sclosedir \"%s\"", 487761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 488761efaa7SDag-Erling Smørgrav handle_to_name(handle)); 489761efaa7SDag-Erling Smørgrav } 490761efaa7SDag-Erling Smørgrav } 491761efaa7SDag-Erling Smørgrav 492761efaa7SDag-Erling Smørgrav static void 493761efaa7SDag-Erling Smørgrav handle_log_exit(void) 494761efaa7SDag-Erling Smørgrav { 495761efaa7SDag-Erling Smørgrav u_int i; 496761efaa7SDag-Erling Smørgrav 497d4af9e69SDag-Erling Smørgrav for (i = 0; i < num_handles; i++) 498761efaa7SDag-Erling Smørgrav if (handles[i].use != HANDLE_UNUSED) 499761efaa7SDag-Erling Smørgrav handle_log_close(i, "forced"); 500761efaa7SDag-Erling Smørgrav } 501761efaa7SDag-Erling Smørgrav 502ae1f160dSDag-Erling Smørgrav static int 503bc5531deSDag-Erling Smørgrav get_handle(struct sshbuf *queue, int *hp) 504b66f2d16SKris Kennaway { 505bc5531deSDag-Erling Smørgrav u_char *handle; 506bc5531deSDag-Erling Smørgrav int r; 507bc5531deSDag-Erling Smørgrav size_t hlen; 5081e8db6e2SBrian Feldman 509bc5531deSDag-Erling Smørgrav *hp = -1; 510bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_string(queue, &handle, &hlen)) != 0) 511bc5531deSDag-Erling Smørgrav return r; 5121e8db6e2SBrian Feldman if (hlen < 256) 513bc5531deSDag-Erling Smørgrav *hp = handle_from_string(handle, hlen); 514e4a9863fSDag-Erling Smørgrav free(handle); 515bc5531deSDag-Erling Smørgrav return 0; 516b66f2d16SKris Kennaway } 517b66f2d16SKris Kennaway 518b66f2d16SKris Kennaway /* send replies */ 519b66f2d16SKris Kennaway 520ae1f160dSDag-Erling Smørgrav static void 521bc5531deSDag-Erling Smørgrav send_msg(struct sshbuf *m) 522b66f2d16SKris Kennaway { 523bc5531deSDag-Erling Smørgrav int r; 5241e8db6e2SBrian Feldman 525bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_stringb(oqueue, m)) != 0) 52619261079SEd Maste fatal_fr(r, "enqueue"); 527bc5531deSDag-Erling Smørgrav sshbuf_reset(m); 528b66f2d16SKris Kennaway } 529b66f2d16SKris Kennaway 530761efaa7SDag-Erling Smørgrav static const char * 531761efaa7SDag-Erling Smørgrav status_to_message(u_int32_t status) 532b66f2d16SKris Kennaway { 5331323ec57SEd Maste static const char * const status_messages[] = { 5341e8db6e2SBrian Feldman "Success", /* SSH_FX_OK */ 5351e8db6e2SBrian Feldman "End of file", /* SSH_FX_EOF */ 5361e8db6e2SBrian Feldman "No such file", /* SSH_FX_NO_SUCH_FILE */ 5371e8db6e2SBrian Feldman "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 5381e8db6e2SBrian Feldman "Failure", /* SSH_FX_FAILURE */ 5391e8db6e2SBrian Feldman "Bad message", /* SSH_FX_BAD_MESSAGE */ 5401e8db6e2SBrian Feldman "No connection", /* SSH_FX_NO_CONNECTION */ 5411e8db6e2SBrian Feldman "Connection lost", /* SSH_FX_CONNECTION_LOST */ 5421e8db6e2SBrian Feldman "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 5431e8db6e2SBrian Feldman "Unknown error" /* Others */ 5441e8db6e2SBrian Feldman }; 545ca86bcf2SDag-Erling Smørgrav return (status_messages[MINIMUM(status,SSH2_FX_MAX)]); 546761efaa7SDag-Erling Smørgrav } 5471e8db6e2SBrian Feldman 548761efaa7SDag-Erling Smørgrav static void 5491323ec57SEd Maste send_status_errmsg(u_int32_t id, u_int32_t status, const char *errmsg) 550761efaa7SDag-Erling Smørgrav { 551bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 552bc5531deSDag-Erling Smørgrav int r; 553761efaa7SDag-Erling Smørgrav 554761efaa7SDag-Erling Smørgrav debug3("request %u: sent status %u", id, status); 555761efaa7SDag-Erling Smørgrav if (log_level > SYSLOG_LEVEL_VERBOSE || 556761efaa7SDag-Erling Smørgrav (status != SSH2_FX_OK && status != SSH2_FX_EOF)) 557761efaa7SDag-Erling Smørgrav logit("sent status %s", status_to_message(status)); 558bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 55919261079SEd Maste fatal_f("sshbuf_new failed"); 560bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_STATUS)) != 0 || 561bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 562bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, status)) != 0) 56319261079SEd Maste fatal_fr(r, "compose"); 5641e8db6e2SBrian Feldman if (version >= 3) { 5651323ec57SEd Maste if ((r = sshbuf_put_cstring(msg, errmsg == NULL ? 5661323ec57SEd Maste status_to_message(status) : errmsg)) != 0 || 567bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "")) != 0) 56819261079SEd Maste fatal_fr(r, "compose message"); 5691e8db6e2SBrian Feldman } 570bc5531deSDag-Erling Smørgrav send_msg(msg); 571bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 572b66f2d16SKris Kennaway } 5731323ec57SEd Maste 5741323ec57SEd Maste static void 5751323ec57SEd Maste send_status(u_int32_t id, u_int32_t status) 5761323ec57SEd Maste { 5771323ec57SEd Maste send_status_errmsg(id, status, NULL); 5781323ec57SEd Maste } 5791323ec57SEd Maste 580ae1f160dSDag-Erling Smørgrav static void 581bc5531deSDag-Erling Smørgrav send_data_or_handle(char type, u_int32_t id, const u_char *data, int dlen) 582b66f2d16SKris Kennaway { 583bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 584bc5531deSDag-Erling Smørgrav int r; 5851e8db6e2SBrian Feldman 586bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 58719261079SEd Maste fatal_f("sshbuf_new failed"); 588bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, type)) != 0 || 589bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 590bc5531deSDag-Erling Smørgrav (r = sshbuf_put_string(msg, data, dlen)) != 0) 59119261079SEd Maste fatal_fr(r, "compose"); 592bc5531deSDag-Erling Smørgrav send_msg(msg); 593bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 594b66f2d16SKris Kennaway } 595b66f2d16SKris Kennaway 596ae1f160dSDag-Erling Smørgrav static void 597bc5531deSDag-Erling Smørgrav send_data(u_int32_t id, const u_char *data, int dlen) 598b66f2d16SKris Kennaway { 599761efaa7SDag-Erling Smørgrav debug("request %u: sent data len %d", id, dlen); 6001e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 601b66f2d16SKris Kennaway } 602b66f2d16SKris Kennaway 603ae1f160dSDag-Erling Smørgrav static void 604b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle) 605b66f2d16SKris Kennaway { 606bc5531deSDag-Erling Smørgrav u_char *string; 607b66f2d16SKris Kennaway int hlen; 6081e8db6e2SBrian Feldman 609b66f2d16SKris Kennaway handle_to_string(handle, &string, &hlen); 610761efaa7SDag-Erling Smørgrav debug("request %u: sent handle handle %d", id, handle); 6111e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 612e4a9863fSDag-Erling Smørgrav free(string); 613b66f2d16SKris Kennaway } 614b66f2d16SKris Kennaway 615ae1f160dSDag-Erling Smørgrav static void 616efcad6b7SDag-Erling Smørgrav send_names(u_int32_t id, int count, const Stat *stats) 617b66f2d16SKris Kennaway { 618bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 619bc5531deSDag-Erling Smørgrav int i, r; 6201e8db6e2SBrian Feldman 621bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 62219261079SEd Maste fatal_f("sshbuf_new failed"); 623bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_NAME)) != 0 || 624bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 625bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, count)) != 0) 62619261079SEd Maste fatal_fr(r, "compose"); 627761efaa7SDag-Erling Smørgrav debug("request %u: sent names count %d", id, count); 628b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 629bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(msg, stats[i].name)) != 0 || 630bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, stats[i].long_name)) != 0 || 631bc5531deSDag-Erling Smørgrav (r = encode_attrib(msg, &stats[i].attrib)) != 0) 63219261079SEd Maste fatal_fr(r, "compose filenames/attrib"); 633b66f2d16SKris Kennaway } 634bc5531deSDag-Erling Smørgrav send_msg(msg); 635bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 636b66f2d16SKris Kennaway } 637b66f2d16SKris Kennaway 638ae1f160dSDag-Erling Smørgrav static void 639efcad6b7SDag-Erling Smørgrav send_attrib(u_int32_t id, const Attrib *a) 640b66f2d16SKris Kennaway { 641bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 642bc5531deSDag-Erling Smørgrav int r; 6431e8db6e2SBrian Feldman 644761efaa7SDag-Erling Smørgrav debug("request %u: sent attrib have 0x%x", id, a->flags); 645bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 64619261079SEd Maste fatal_f("sshbuf_new failed"); 647bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_ATTRS)) != 0 || 648bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 649bc5531deSDag-Erling Smørgrav (r = encode_attrib(msg, a)) != 0) 65019261079SEd Maste fatal_fr(r, "compose"); 651bc5531deSDag-Erling Smørgrav send_msg(msg); 652bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 653b66f2d16SKris Kennaway } 654b66f2d16SKris Kennaway 655d4af9e69SDag-Erling Smørgrav static void 656d4af9e69SDag-Erling Smørgrav send_statvfs(u_int32_t id, struct statvfs *st) 657d4af9e69SDag-Erling Smørgrav { 658bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 659d4af9e69SDag-Erling Smørgrav u_int64_t flag; 660bc5531deSDag-Erling Smørgrav int r; 661d4af9e69SDag-Erling Smørgrav 662d4af9e69SDag-Erling Smørgrav flag = (st->f_flag & ST_RDONLY) ? SSH2_FXE_STATVFS_ST_RDONLY : 0; 663d4af9e69SDag-Erling Smørgrav flag |= (st->f_flag & ST_NOSUID) ? SSH2_FXE_STATVFS_ST_NOSUID : 0; 664d4af9e69SDag-Erling Smørgrav 665bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 66619261079SEd Maste fatal_f("sshbuf_new failed"); 667bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED_REPLY)) != 0 || 668bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 669bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_bsize)) != 0 || 670bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_frsize)) != 0 || 671bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_blocks)) != 0 || 672bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_bfree)) != 0 || 673bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_bavail)) != 0 || 674bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_files)) != 0 || 675bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_ffree)) != 0 || 676bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_favail)) != 0 || 677bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, FSID_TO_ULONG(st->f_fsid))) != 0 || 678bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, flag)) != 0 || 679bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_namemax)) != 0) 68019261079SEd Maste fatal_fr(r, "compose"); 681bc5531deSDag-Erling Smørgrav send_msg(msg); 682bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 683d4af9e69SDag-Erling Smørgrav } 684d4af9e69SDag-Erling Smørgrav 68519261079SEd Maste /* 68619261079SEd Maste * Prepare SSH2_FXP_VERSION extension advertisement for a single extension. 6871323ec57SEd Maste * The extension is checked for permission prior to advertisement. 68819261079SEd Maste */ 68919261079SEd Maste static int 69019261079SEd Maste compose_extension(struct sshbuf *msg, const char *name, const char *ver) 69119261079SEd Maste { 69219261079SEd Maste int r; 69319261079SEd Maste const struct sftp_handler *exthnd; 69419261079SEd Maste 69519261079SEd Maste if ((exthnd = extended_handler_byname(name)) == NULL) 69619261079SEd Maste fatal_f("internal error: no handler for %s", name); 69719261079SEd Maste if (!request_permitted(exthnd)) { 69819261079SEd Maste debug2_f("refusing to advertise disallowed extension %s", name); 69919261079SEd Maste return 0; 70019261079SEd Maste } 70119261079SEd Maste if ((r = sshbuf_put_cstring(msg, name)) != 0 || 70219261079SEd Maste (r = sshbuf_put_cstring(msg, ver)) != 0) 70319261079SEd Maste fatal_fr(r, "compose %s", name); 70419261079SEd Maste return 0; 70519261079SEd Maste } 70619261079SEd Maste 707b66f2d16SKris Kennaway /* parse incoming */ 708b66f2d16SKris Kennaway 709ae1f160dSDag-Erling Smørgrav static void 710b66f2d16SKris Kennaway process_init(void) 711b66f2d16SKris Kennaway { 712bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 713bc5531deSDag-Erling Smørgrav int r; 714b66f2d16SKris Kennaway 715bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(iqueue, &version)) != 0) 71619261079SEd Maste fatal_fr(r, "parse"); 717e146993eSDag-Erling Smørgrav verbose("received client version %u", version); 718bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 71919261079SEd Maste fatal_f("sshbuf_new failed"); 720bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_VERSION)) != 0 || 72119261079SEd Maste (r = sshbuf_put_u32(msg, SSH2_FILEXFER_VERSION)) != 0) 72219261079SEd Maste fatal_fr(r, "compose"); 72319261079SEd Maste 7241323ec57SEd Maste /* extension advertisements */ 72519261079SEd Maste compose_extension(msg, "posix-rename@openssh.com", "1"); 72619261079SEd Maste compose_extension(msg, "statvfs@openssh.com", "2"); 72719261079SEd Maste compose_extension(msg, "fstatvfs@openssh.com", "2"); 72819261079SEd Maste compose_extension(msg, "hardlink@openssh.com", "1"); 72919261079SEd Maste compose_extension(msg, "fsync@openssh.com", "1"); 73019261079SEd Maste compose_extension(msg, "lsetstat@openssh.com", "1"); 73119261079SEd Maste compose_extension(msg, "limits@openssh.com", "1"); 73219261079SEd Maste compose_extension(msg, "expand-path@openssh.com", "1"); 73387c1498dSEd Maste compose_extension(msg, "copy-data", "1"); 73438a52bd3SEd Maste compose_extension(msg, "home-directory", "1"); 73538a52bd3SEd Maste compose_extension(msg, "users-groups-by-id@openssh.com", "1"); 73619261079SEd Maste 737bc5531deSDag-Erling Smørgrav send_msg(msg); 738bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 739b66f2d16SKris Kennaway } 740b66f2d16SKris Kennaway 741ae1f160dSDag-Erling Smørgrav static void 742f7167e0eSDag-Erling Smørgrav process_open(u_int32_t id) 743b66f2d16SKris Kennaway { 744f7167e0eSDag-Erling Smørgrav u_int32_t pflags; 745bc5531deSDag-Erling Smørgrav Attrib a; 746b66f2d16SKris Kennaway char *name; 747bc5531deSDag-Erling Smørgrav int r, handle, fd, flags, mode, status = SSH2_FX_FAILURE; 748b66f2d16SKris Kennaway 749bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 750bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u32(iqueue, &pflags)) != 0 || /* portable flags */ 751bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 75219261079SEd Maste fatal_fr(r, "parse"); 753bc5531deSDag-Erling Smørgrav 754761efaa7SDag-Erling Smørgrav debug3("request %u: open flags %d", id, pflags); 755b66f2d16SKris Kennaway flags = flags_from_portable(pflags); 756bc5531deSDag-Erling Smørgrav mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a.perm : 0666; 757761efaa7SDag-Erling Smørgrav logit("open \"%s\" flags %s mode 0%o", 758761efaa7SDag-Erling Smørgrav name, string_from_portable(pflags), mode); 759b15c8340SDag-Erling Smørgrav if (readonly && 7604f52dfbbSDag-Erling Smørgrav ((flags & O_ACCMODE) != O_RDONLY || 7614f52dfbbSDag-Erling Smørgrav (flags & (O_CREAT|O_TRUNC)) != 0)) { 762f7167e0eSDag-Erling Smørgrav verbose("Refusing open request in read-only mode"); 763b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 764f7167e0eSDag-Erling Smørgrav } else { 765b66f2d16SKris Kennaway fd = open(name, flags, mode); 76619261079SEd Maste if (fd == -1) { 767b66f2d16SKris Kennaway status = errno_to_portable(errno); 768b66f2d16SKris Kennaway } else { 769f7167e0eSDag-Erling Smørgrav handle = handle_new(HANDLE_FILE, name, fd, flags, NULL); 770b66f2d16SKris Kennaway if (handle < 0) { 771b66f2d16SKris Kennaway close(fd); 772b66f2d16SKris Kennaway } else { 773b66f2d16SKris Kennaway send_handle(id, handle); 7741e8db6e2SBrian Feldman status = SSH2_FX_OK; 775b66f2d16SKris Kennaway } 776b66f2d16SKris Kennaway } 777b15c8340SDag-Erling Smørgrav } 7781e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 779b66f2d16SKris Kennaway send_status(id, status); 780e4a9863fSDag-Erling Smørgrav free(name); 781b66f2d16SKris Kennaway } 782b66f2d16SKris Kennaway 783ae1f160dSDag-Erling Smørgrav static void 784f7167e0eSDag-Erling Smørgrav process_close(u_int32_t id) 785b66f2d16SKris Kennaway { 786bc5531deSDag-Erling Smørgrav int r, handle, ret, status = SSH2_FX_FAILURE; 787b66f2d16SKris Kennaway 788bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 78919261079SEd Maste fatal_fr(r, "parse"); 790bc5531deSDag-Erling Smørgrav 791761efaa7SDag-Erling Smørgrav debug3("request %u: close handle %u", id, handle); 792761efaa7SDag-Erling Smørgrav handle_log_close(handle, NULL); 793b66f2d16SKris Kennaway ret = handle_close(handle); 7941e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 795b66f2d16SKris Kennaway send_status(id, status); 796b66f2d16SKris Kennaway } 797b66f2d16SKris Kennaway 798ae1f160dSDag-Erling Smørgrav static void 799f7167e0eSDag-Erling Smørgrav process_read(u_int32_t id) 800b66f2d16SKris Kennaway { 80119261079SEd Maste static u_char *buf; 80219261079SEd Maste static size_t buflen; 803f7167e0eSDag-Erling Smørgrav u_int32_t len; 804bc5531deSDag-Erling Smørgrav int r, handle, fd, ret, status = SSH2_FX_FAILURE; 805b66f2d16SKris Kennaway u_int64_t off; 806b66f2d16SKris Kennaway 807bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0 || 808bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u64(iqueue, &off)) != 0 || 809bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u32(iqueue, &len)) != 0) 81019261079SEd Maste fatal_fr(r, "parse"); 811b66f2d16SKris Kennaway 81219261079SEd Maste debug("request %u: read \"%s\" (handle %d) off %llu len %u", 813761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len); 81419261079SEd Maste if ((fd = handle_to_fd(handle)) == -1) 81519261079SEd Maste goto out; 81619261079SEd Maste if (len > SFTP_MAX_READ_LENGTH) { 81719261079SEd Maste debug2("read change len %u to %u", len, SFTP_MAX_READ_LENGTH); 81819261079SEd Maste len = SFTP_MAX_READ_LENGTH; 819b66f2d16SKris Kennaway } 82019261079SEd Maste if (len > buflen) { 82119261079SEd Maste debug3_f("allocate %zu => %u", buflen, len); 822*4d3fc8b0SEd Maste if ((buf = realloc(buf, len)) == NULL) 82319261079SEd Maste fatal_f("realloc failed"); 82419261079SEd Maste buflen = len; 82519261079SEd Maste } 82619261079SEd Maste if (lseek(fd, off, SEEK_SET) == -1) { 827b66f2d16SKris Kennaway status = errno_to_portable(errno); 82819261079SEd Maste error_f("seek \"%.100s\": %s", handle_to_name(handle), 82919261079SEd Maste strerror(errno)); 83019261079SEd Maste goto out; 83119261079SEd Maste } 83219261079SEd Maste if (len == 0) { 83319261079SEd Maste /* weird, but not strictly disallowed */ 83419261079SEd Maste ret = 0; 83519261079SEd Maste } else if ((ret = read(fd, buf, len)) == -1) { 836b66f2d16SKris Kennaway status = errno_to_portable(errno); 83719261079SEd Maste error_f("read \"%.100s\": %s", handle_to_name(handle), 83819261079SEd Maste strerror(errno)); 83919261079SEd Maste goto out; 840b66f2d16SKris Kennaway } else if (ret == 0) { 8411e8db6e2SBrian Feldman status = SSH2_FX_EOF; 84219261079SEd Maste goto out; 84319261079SEd Maste } 844b66f2d16SKris Kennaway send_data(id, buf, ret); 845761efaa7SDag-Erling Smørgrav handle_update_read(handle, ret); 84619261079SEd Maste /* success */ 84719261079SEd Maste status = SSH2_FX_OK; 84819261079SEd Maste out: 8491e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 850b66f2d16SKris Kennaway send_status(id, status); 851b66f2d16SKris Kennaway } 852b66f2d16SKris Kennaway 853ae1f160dSDag-Erling Smørgrav static void 854f7167e0eSDag-Erling Smørgrav process_write(u_int32_t id) 855b66f2d16SKris Kennaway { 856b66f2d16SKris Kennaway u_int64_t off; 857bc5531deSDag-Erling Smørgrav size_t len; 858bc5531deSDag-Erling Smørgrav int r, handle, fd, ret, status; 859bc5531deSDag-Erling Smørgrav u_char *data; 860b66f2d16SKris Kennaway 861bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0 || 862bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u64(iqueue, &off)) != 0 || 863bc5531deSDag-Erling Smørgrav (r = sshbuf_get_string(iqueue, &data, &len)) != 0) 86419261079SEd Maste fatal_fr(r, "parse"); 865b66f2d16SKris Kennaway 866bc5531deSDag-Erling Smørgrav debug("request %u: write \"%s\" (handle %d) off %llu len %zu", 867761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len); 868b66f2d16SKris Kennaway fd = handle_to_fd(handle); 869b15c8340SDag-Erling Smørgrav 870b15c8340SDag-Erling Smørgrav if (fd < 0) 871b15c8340SDag-Erling Smørgrav status = SSH2_FX_FAILURE; 872b15c8340SDag-Erling Smørgrav else { 873f7167e0eSDag-Erling Smørgrav if (!(handle_to_flags(handle) & O_APPEND) && 87419261079SEd Maste lseek(fd, off, SEEK_SET) == -1) { 875b66f2d16SKris Kennaway status = errno_to_portable(errno); 87619261079SEd Maste error_f("seek \"%.100s\": %s", handle_to_name(handle), 87719261079SEd Maste strerror(errno)); 878b66f2d16SKris Kennaway } else { 879b66f2d16SKris Kennaway /* XXX ATOMICIO ? */ 880b66f2d16SKris Kennaway ret = write(fd, data, len); 88119261079SEd Maste if (ret == -1) { 882b66f2d16SKris Kennaway status = errno_to_portable(errno); 88319261079SEd Maste error_f("write \"%.100s\": %s", 88419261079SEd Maste handle_to_name(handle), strerror(errno)); 885043840dfSDag-Erling Smørgrav } else if ((size_t)ret == len) { 8861e8db6e2SBrian Feldman status = SSH2_FX_OK; 887761efaa7SDag-Erling Smørgrav handle_update_write(handle, ret); 888b66f2d16SKris Kennaway } else { 88919261079SEd Maste debug2_f("nothing at all written"); 890b15c8340SDag-Erling Smørgrav status = SSH2_FX_FAILURE; 891b66f2d16SKris Kennaway } 892b66f2d16SKris Kennaway } 893b66f2d16SKris Kennaway } 894b66f2d16SKris Kennaway send_status(id, status); 895e4a9863fSDag-Erling Smørgrav free(data); 896b66f2d16SKris Kennaway } 897b66f2d16SKris Kennaway 898ae1f160dSDag-Erling Smørgrav static void 899f7167e0eSDag-Erling Smørgrav process_do_stat(u_int32_t id, int do_lstat) 900b66f2d16SKris Kennaway { 9011e8db6e2SBrian Feldman Attrib a; 902b66f2d16SKris Kennaway struct stat st; 903b66f2d16SKris Kennaway char *name; 904bc5531deSDag-Erling Smørgrav int r, status = SSH2_FX_FAILURE; 905b66f2d16SKris Kennaway 906bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) 90719261079SEd Maste fatal_fr(r, "parse"); 908bc5531deSDag-Erling Smørgrav 909761efaa7SDag-Erling Smørgrav debug3("request %u: %sstat", id, do_lstat ? "l" : ""); 910761efaa7SDag-Erling Smørgrav verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); 911bc5531deSDag-Erling Smørgrav r = do_lstat ? lstat(name, &st) : stat(name, &st); 91219261079SEd Maste if (r == -1) { 913b66f2d16SKris Kennaway status = errno_to_portable(errno); 914b66f2d16SKris Kennaway } else { 9151e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 9161e8db6e2SBrian Feldman send_attrib(id, &a); 9171e8db6e2SBrian Feldman status = SSH2_FX_OK; 918b66f2d16SKris Kennaway } 9191e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 920b66f2d16SKris Kennaway send_status(id, status); 921e4a9863fSDag-Erling Smørgrav free(name); 922b66f2d16SKris Kennaway } 923b66f2d16SKris Kennaway 924ae1f160dSDag-Erling Smørgrav static void 925f7167e0eSDag-Erling Smørgrav process_stat(u_int32_t id) 926b66f2d16SKris Kennaway { 927f7167e0eSDag-Erling Smørgrav process_do_stat(id, 0); 928b66f2d16SKris Kennaway } 929b66f2d16SKris Kennaway 930ae1f160dSDag-Erling Smørgrav static void 931f7167e0eSDag-Erling Smørgrav process_lstat(u_int32_t id) 932b66f2d16SKris Kennaway { 933f7167e0eSDag-Erling Smørgrav process_do_stat(id, 1); 934b66f2d16SKris Kennaway } 935b66f2d16SKris Kennaway 936ae1f160dSDag-Erling Smørgrav static void 937f7167e0eSDag-Erling Smørgrav process_fstat(u_int32_t id) 938b66f2d16SKris Kennaway { 9391e8db6e2SBrian Feldman Attrib a; 940b66f2d16SKris Kennaway struct stat st; 941bc5531deSDag-Erling Smørgrav int fd, r, handle, status = SSH2_FX_FAILURE; 942b66f2d16SKris Kennaway 943bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 94419261079SEd Maste fatal_fr(r, "parse"); 945761efaa7SDag-Erling Smørgrav debug("request %u: fstat \"%s\" (handle %u)", 946761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle); 947b66f2d16SKris Kennaway fd = handle_to_fd(handle); 948b66f2d16SKris Kennaway if (fd >= 0) { 949bc5531deSDag-Erling Smørgrav r = fstat(fd, &st); 95019261079SEd Maste if (r == -1) { 951b66f2d16SKris Kennaway status = errno_to_portable(errno); 952b66f2d16SKris Kennaway } else { 9531e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 9541e8db6e2SBrian Feldman send_attrib(id, &a); 9551e8db6e2SBrian Feldman status = SSH2_FX_OK; 956b66f2d16SKris Kennaway } 957b66f2d16SKris Kennaway } 9581e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 959b66f2d16SKris Kennaway send_status(id, status); 960b66f2d16SKris Kennaway } 961b66f2d16SKris Kennaway 962ae1f160dSDag-Erling Smørgrav static struct timeval * 963efcad6b7SDag-Erling Smørgrav attrib_to_tv(const Attrib *a) 964b66f2d16SKris Kennaway { 965b66f2d16SKris Kennaway static struct timeval tv[2]; 9661e8db6e2SBrian Feldman 967b66f2d16SKris Kennaway tv[0].tv_sec = a->atime; 968b66f2d16SKris Kennaway tv[0].tv_usec = 0; 969b66f2d16SKris Kennaway tv[1].tv_sec = a->mtime; 970b66f2d16SKris Kennaway tv[1].tv_usec = 0; 971b66f2d16SKris Kennaway return tv; 972b66f2d16SKris Kennaway } 973b66f2d16SKris Kennaway 97419261079SEd Maste static struct timespec * 97519261079SEd Maste attrib_to_ts(const Attrib *a) 97619261079SEd Maste { 97719261079SEd Maste static struct timespec ts[2]; 97819261079SEd Maste 97919261079SEd Maste ts[0].tv_sec = a->atime; 98019261079SEd Maste ts[0].tv_nsec = 0; 98119261079SEd Maste ts[1].tv_sec = a->mtime; 98219261079SEd Maste ts[1].tv_nsec = 0; 98319261079SEd Maste return ts; 98419261079SEd Maste } 98519261079SEd Maste 986ae1f160dSDag-Erling Smørgrav static void 987f7167e0eSDag-Erling Smørgrav process_setstat(u_int32_t id) 988b66f2d16SKris Kennaway { 989bc5531deSDag-Erling Smørgrav Attrib a; 990b66f2d16SKris Kennaway char *name; 991bc5531deSDag-Erling Smørgrav int r, status = SSH2_FX_OK; 992b66f2d16SKris Kennaway 993bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 994bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 99519261079SEd Maste fatal_fr(r, "parse"); 996bc5531deSDag-Erling Smørgrav 997761efaa7SDag-Erling Smørgrav debug("request %u: setstat name \"%s\"", id, name); 998bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { 999d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu", 1000bc5531deSDag-Erling Smørgrav name, (unsigned long long)a.size); 1001bc5531deSDag-Erling Smørgrav r = truncate(name, a.size); 1002bc5531deSDag-Erling Smørgrav if (r == -1) 1003ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 1004ae1f160dSDag-Erling Smørgrav } 1005bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 1006bc5531deSDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a.perm); 1007bc5531deSDag-Erling Smørgrav r = chmod(name, a.perm & 07777); 1008bc5531deSDag-Erling Smørgrav if (r == -1) 1009b66f2d16SKris Kennaway status = errno_to_portable(errno); 1010b66f2d16SKris Kennaway } 1011bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 1012761efaa7SDag-Erling Smørgrav char buf[64]; 1013bc5531deSDag-Erling Smørgrav time_t t = a.mtime; 1014761efaa7SDag-Erling Smørgrav 1015761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 1016761efaa7SDag-Erling Smørgrav localtime(&t)); 1017761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf); 1018bc5531deSDag-Erling Smørgrav r = utimes(name, attrib_to_tv(&a)); 1019bc5531deSDag-Erling Smørgrav if (r == -1) 1020b66f2d16SKris Kennaway status = errno_to_portable(errno); 1021b66f2d16SKris Kennaway } 1022bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { 1023761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name, 1024bc5531deSDag-Erling Smørgrav (u_long)a.uid, (u_long)a.gid); 1025bc5531deSDag-Erling Smørgrav r = chown(name, a.uid, a.gid); 1026bc5531deSDag-Erling Smørgrav if (r == -1) 10271e8db6e2SBrian Feldman status = errno_to_portable(errno); 10281e8db6e2SBrian Feldman } 1029b66f2d16SKris Kennaway send_status(id, status); 1030e4a9863fSDag-Erling Smørgrav free(name); 1031b66f2d16SKris Kennaway } 1032b66f2d16SKris Kennaway 1033ae1f160dSDag-Erling Smørgrav static void 1034f7167e0eSDag-Erling Smørgrav process_fsetstat(u_int32_t id) 1035b66f2d16SKris Kennaway { 1036bc5531deSDag-Erling Smørgrav Attrib a; 1037bc5531deSDag-Erling Smørgrav int handle, fd, r; 10381e8db6e2SBrian Feldman int status = SSH2_FX_OK; 1039b66f2d16SKris Kennaway 1040bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0 || 1041bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 104219261079SEd Maste fatal_fr(r, "parse"); 1043bc5531deSDag-Erling Smørgrav 1044761efaa7SDag-Erling Smørgrav debug("request %u: fsetstat handle %d", id, handle); 1045b66f2d16SKris Kennaway fd = handle_to_fd(handle); 1046b15c8340SDag-Erling Smørgrav if (fd < 0) 10471e8db6e2SBrian Feldman status = SSH2_FX_FAILURE; 1048b15c8340SDag-Erling Smørgrav else { 1049761efaa7SDag-Erling Smørgrav char *name = handle_to_name(handle); 1050761efaa7SDag-Erling Smørgrav 1051bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { 1052d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu", 1053bc5531deSDag-Erling Smørgrav name, (unsigned long long)a.size); 1054bc5531deSDag-Erling Smørgrav r = ftruncate(fd, a.size); 1055bc5531deSDag-Erling Smørgrav if (r == -1) 1056ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 1057ae1f160dSDag-Erling Smørgrav } 1058bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 1059bc5531deSDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a.perm); 106083d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 1061bc5531deSDag-Erling Smørgrav r = fchmod(fd, a.perm & 07777); 106283d2307dSDag-Erling Smørgrav #else 1063bc5531deSDag-Erling Smørgrav r = chmod(name, a.perm & 07777); 106483d2307dSDag-Erling Smørgrav #endif 1065bc5531deSDag-Erling Smørgrav if (r == -1) 1066b66f2d16SKris Kennaway status = errno_to_portable(errno); 1067b66f2d16SKris Kennaway } 1068bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 1069761efaa7SDag-Erling Smørgrav char buf[64]; 1070bc5531deSDag-Erling Smørgrav time_t t = a.mtime; 1071761efaa7SDag-Erling Smørgrav 1072761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 1073761efaa7SDag-Erling Smørgrav localtime(&t)); 1074761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf); 107583d2307dSDag-Erling Smørgrav #ifdef HAVE_FUTIMES 1076bc5531deSDag-Erling Smørgrav r = futimes(fd, attrib_to_tv(&a)); 107783d2307dSDag-Erling Smørgrav #else 1078bc5531deSDag-Erling Smørgrav r = utimes(name, attrib_to_tv(&a)); 107983d2307dSDag-Erling Smørgrav #endif 1080bc5531deSDag-Erling Smørgrav if (r == -1) 1081b66f2d16SKris Kennaway status = errno_to_portable(errno); 1082b66f2d16SKris Kennaway } 1083bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { 1084761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name, 1085bc5531deSDag-Erling Smørgrav (u_long)a.uid, (u_long)a.gid); 108683d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHOWN 1087bc5531deSDag-Erling Smørgrav r = fchown(fd, a.uid, a.gid); 108883d2307dSDag-Erling Smørgrav #else 1089bc5531deSDag-Erling Smørgrav r = chown(name, a.uid, a.gid); 109083d2307dSDag-Erling Smørgrav #endif 1091bc5531deSDag-Erling Smørgrav if (r == -1) 10921e8db6e2SBrian Feldman status = errno_to_portable(errno); 10931e8db6e2SBrian Feldman } 1094b66f2d16SKris Kennaway } 1095b66f2d16SKris Kennaway send_status(id, status); 1096b66f2d16SKris Kennaway } 1097b66f2d16SKris Kennaway 1098ae1f160dSDag-Erling Smørgrav static void 1099f7167e0eSDag-Erling Smørgrav process_opendir(u_int32_t id) 1100b66f2d16SKris Kennaway { 1101b66f2d16SKris Kennaway DIR *dirp = NULL; 1102b66f2d16SKris Kennaway char *path; 1103bc5531deSDag-Erling Smørgrav int r, handle, status = SSH2_FX_FAILURE; 1104b66f2d16SKris Kennaway 1105bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 110619261079SEd Maste fatal_fr(r, "parse"); 1107bc5531deSDag-Erling Smørgrav 1108761efaa7SDag-Erling Smørgrav debug3("request %u: opendir", id); 1109761efaa7SDag-Erling Smørgrav logit("opendir \"%s\"", path); 1110b66f2d16SKris Kennaway dirp = opendir(path); 1111b66f2d16SKris Kennaway if (dirp == NULL) { 1112b66f2d16SKris Kennaway status = errno_to_portable(errno); 1113b66f2d16SKris Kennaway } else { 1114f7167e0eSDag-Erling Smørgrav handle = handle_new(HANDLE_DIR, path, 0, 0, dirp); 1115b66f2d16SKris Kennaway if (handle < 0) { 1116b66f2d16SKris Kennaway closedir(dirp); 1117b66f2d16SKris Kennaway } else { 1118b66f2d16SKris Kennaway send_handle(id, handle); 11191e8db6e2SBrian Feldman status = SSH2_FX_OK; 1120b66f2d16SKris Kennaway } 1121b66f2d16SKris Kennaway 1122b66f2d16SKris Kennaway } 11231e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 1124b66f2d16SKris Kennaway send_status(id, status); 1125e4a9863fSDag-Erling Smørgrav free(path); 1126b66f2d16SKris Kennaway } 1127b66f2d16SKris Kennaway 1128ae1f160dSDag-Erling Smørgrav static void 1129f7167e0eSDag-Erling Smørgrav process_readdir(u_int32_t id) 1130b66f2d16SKris Kennaway { 1131b66f2d16SKris Kennaway DIR *dirp; 1132b66f2d16SKris Kennaway struct dirent *dp; 1133b66f2d16SKris Kennaway char *path; 1134bc5531deSDag-Erling Smørgrav int r, handle; 1135b66f2d16SKris Kennaway 1136bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 113719261079SEd Maste fatal_fr(r, "parse"); 1138bc5531deSDag-Erling Smørgrav 1139761efaa7SDag-Erling Smørgrav debug("request %u: readdir \"%s\" (handle %d)", id, 1140761efaa7SDag-Erling Smørgrav handle_to_name(handle), handle); 1141b66f2d16SKris Kennaway dirp = handle_to_dir(handle); 1142b66f2d16SKris Kennaway path = handle_to_name(handle); 1143b66f2d16SKris Kennaway if (dirp == NULL || path == NULL) { 11441e8db6e2SBrian Feldman send_status(id, SSH2_FX_FAILURE); 1145b66f2d16SKris Kennaway } else { 1146b66f2d16SKris Kennaway struct stat st; 1147bc5531deSDag-Erling Smørgrav char pathname[PATH_MAX]; 1148b66f2d16SKris Kennaway Stat *stats; 1149b66f2d16SKris Kennaway int nstats = 10, count = 0, i; 1150ee21a45fSDag-Erling Smørgrav 1151761efaa7SDag-Erling Smørgrav stats = xcalloc(nstats, sizeof(Stat)); 1152b66f2d16SKris Kennaway while ((dp = readdir(dirp)) != NULL) { 1153b66f2d16SKris Kennaway if (count >= nstats) { 1154b66f2d16SKris Kennaway nstats *= 2; 1155557f75e5SDag-Erling Smørgrav stats = xreallocarray(stats, nstats, sizeof(Stat)); 1156b66f2d16SKris Kennaway } 1157b66f2d16SKris Kennaway /* XXX OVERFLOW ? */ 1158ae1f160dSDag-Erling Smørgrav snprintf(pathname, sizeof pathname, "%s%s%s", path, 1159ae1f160dSDag-Erling Smørgrav strcmp(path, "/") ? "/" : "", dp->d_name); 116019261079SEd Maste if (lstat(pathname, &st) == -1) 1161b66f2d16SKris Kennaway continue; 11621e8db6e2SBrian Feldman stat_to_attrib(&st, &(stats[count].attrib)); 1163b66f2d16SKris Kennaway stats[count].name = xstrdup(dp->d_name); 116438a52bd3SEd Maste stats[count].long_name = ls_file(dp->d_name, &st, 116538a52bd3SEd Maste 0, 0, NULL, NULL); 1166b66f2d16SKris Kennaway count++; 1167b66f2d16SKris Kennaway /* send up to 100 entries in one message */ 11681e8db6e2SBrian Feldman /* XXX check packet size instead */ 1169b66f2d16SKris Kennaway if (count == 100) 1170b66f2d16SKris Kennaway break; 1171b66f2d16SKris Kennaway } 11721e8db6e2SBrian Feldman if (count > 0) { 1173b66f2d16SKris Kennaway send_names(id, count, stats); 1174b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 1175e4a9863fSDag-Erling Smørgrav free(stats[i].name); 1176e4a9863fSDag-Erling Smørgrav free(stats[i].long_name); 1177b66f2d16SKris Kennaway } 11781e8db6e2SBrian Feldman } else { 11791e8db6e2SBrian Feldman send_status(id, SSH2_FX_EOF); 11801e8db6e2SBrian Feldman } 1181e4a9863fSDag-Erling Smørgrav free(stats); 1182b66f2d16SKris Kennaway } 1183b66f2d16SKris Kennaway } 1184b66f2d16SKris Kennaway 1185ae1f160dSDag-Erling Smørgrav static void 1186f7167e0eSDag-Erling Smørgrav process_remove(u_int32_t id) 1187b66f2d16SKris Kennaway { 1188b66f2d16SKris Kennaway char *name; 1189bc5531deSDag-Erling Smørgrav int r, status = SSH2_FX_FAILURE; 1190b66f2d16SKris Kennaway 1191bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) 119219261079SEd Maste fatal_fr(r, "parse"); 1193bc5531deSDag-Erling Smørgrav 1194761efaa7SDag-Erling Smørgrav debug3("request %u: remove", id); 1195761efaa7SDag-Erling Smørgrav logit("remove name \"%s\"", name); 1196bc5531deSDag-Erling Smørgrav r = unlink(name); 1197bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1198b66f2d16SKris Kennaway send_status(id, status); 1199e4a9863fSDag-Erling Smørgrav free(name); 1200b66f2d16SKris Kennaway } 1201b66f2d16SKris Kennaway 1202ae1f160dSDag-Erling Smørgrav static void 1203f7167e0eSDag-Erling Smørgrav process_mkdir(u_int32_t id) 1204b66f2d16SKris Kennaway { 1205bc5531deSDag-Erling Smørgrav Attrib a; 1206b66f2d16SKris Kennaway char *name; 1207bc5531deSDag-Erling Smørgrav int r, mode, status = SSH2_FX_FAILURE; 1208b66f2d16SKris Kennaway 1209bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 1210bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 121119261079SEd Maste fatal_fr(r, "parse"); 1212bc5531deSDag-Erling Smørgrav 1213bc5531deSDag-Erling Smørgrav mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 1214bc5531deSDag-Erling Smørgrav a.perm & 07777 : 0777; 1215761efaa7SDag-Erling Smørgrav debug3("request %u: mkdir", id); 1216761efaa7SDag-Erling Smørgrav logit("mkdir name \"%s\" mode 0%o", name, mode); 1217bc5531deSDag-Erling Smørgrav r = mkdir(name, mode); 1218bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1219b66f2d16SKris Kennaway send_status(id, status); 1220e4a9863fSDag-Erling Smørgrav free(name); 1221b66f2d16SKris Kennaway } 1222b66f2d16SKris Kennaway 1223ae1f160dSDag-Erling Smørgrav static void 1224f7167e0eSDag-Erling Smørgrav process_rmdir(u_int32_t id) 1225b66f2d16SKris Kennaway { 1226b66f2d16SKris Kennaway char *name; 1227bc5531deSDag-Erling Smørgrav int r, status; 1228b66f2d16SKris Kennaway 1229bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) 123019261079SEd Maste fatal_fr(r, "parse"); 1231bc5531deSDag-Erling Smørgrav 1232761efaa7SDag-Erling Smørgrav debug3("request %u: rmdir", id); 1233761efaa7SDag-Erling Smørgrav logit("rmdir name \"%s\"", name); 1234bc5531deSDag-Erling Smørgrav r = rmdir(name); 1235bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1236b66f2d16SKris Kennaway send_status(id, status); 1237e4a9863fSDag-Erling Smørgrav free(name); 1238b66f2d16SKris Kennaway } 1239b66f2d16SKris Kennaway 1240ae1f160dSDag-Erling Smørgrav static void 1241f7167e0eSDag-Erling Smørgrav process_realpath(u_int32_t id) 1242b66f2d16SKris Kennaway { 1243bc5531deSDag-Erling Smørgrav char resolvedname[PATH_MAX]; 1244b66f2d16SKris Kennaway char *path; 1245bc5531deSDag-Erling Smørgrav int r; 1246b66f2d16SKris Kennaway 1247bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 124819261079SEd Maste fatal_fr(r, "parse"); 1249bc5531deSDag-Erling Smørgrav 12501e8db6e2SBrian Feldman if (path[0] == '\0') { 1251e4a9863fSDag-Erling Smørgrav free(path); 12521e8db6e2SBrian Feldman path = xstrdup("."); 12531e8db6e2SBrian Feldman } 1254761efaa7SDag-Erling Smørgrav debug3("request %u: realpath", id); 1255761efaa7SDag-Erling Smørgrav verbose("realpath \"%s\"", path); 125619261079SEd Maste if (sftp_realpath(path, resolvedname) == NULL) { 1257b66f2d16SKris Kennaway send_status(id, errno_to_portable(errno)); 1258b66f2d16SKris Kennaway } else { 1259b66f2d16SKris Kennaway Stat s; 1260b66f2d16SKris Kennaway attrib_clear(&s.attrib); 1261b66f2d16SKris Kennaway s.name = s.long_name = resolvedname; 1262b66f2d16SKris Kennaway send_names(id, 1, &s); 1263b66f2d16SKris Kennaway } 1264e4a9863fSDag-Erling Smørgrav free(path); 1265b66f2d16SKris Kennaway } 1266b66f2d16SKris Kennaway 1267ae1f160dSDag-Erling Smørgrav static void 1268f7167e0eSDag-Erling Smørgrav process_rename(u_int32_t id) 1269b66f2d16SKris Kennaway { 1270b66f2d16SKris Kennaway char *oldpath, *newpath; 1271bc5531deSDag-Erling Smørgrav int r, status; 1272d0c8c0bcSDag-Erling Smørgrav struct stat sb; 1273b66f2d16SKris Kennaway 1274bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1275bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 127619261079SEd Maste fatal_fr(r, "parse"); 1277bc5531deSDag-Erling Smørgrav 1278761efaa7SDag-Erling Smørgrav debug3("request %u: rename", id); 1279761efaa7SDag-Erling Smørgrav logit("rename old \"%s\" new \"%s\"", oldpath, newpath); 1280d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_FAILURE; 1281f7167e0eSDag-Erling Smørgrav if (lstat(oldpath, &sb) == -1) 1282d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1283d0c8c0bcSDag-Erling Smørgrav else if (S_ISREG(sb.st_mode)) { 1284d0c8c0bcSDag-Erling Smørgrav /* Race-free rename of regular files */ 1285d74d50a8SDag-Erling Smørgrav if (link(oldpath, newpath) == -1) { 12867aee6ffeSDag-Erling Smørgrav if (errno == EOPNOTSUPP || errno == ENOSYS 1287d4af9e69SDag-Erling Smørgrav #ifdef EXDEV 1288d4af9e69SDag-Erling Smørgrav || errno == EXDEV 1289d4af9e69SDag-Erling Smørgrav #endif 1290d74d50a8SDag-Erling Smørgrav #ifdef LINK_OPNOTSUPP_ERRNO 1291d74d50a8SDag-Erling Smørgrav || errno == LINK_OPNOTSUPP_ERRNO 1292d74d50a8SDag-Erling Smørgrav #endif 1293d74d50a8SDag-Erling Smørgrav ) { 1294d74d50a8SDag-Erling Smørgrav struct stat st; 1295d74d50a8SDag-Erling Smørgrav 1296d74d50a8SDag-Erling Smørgrav /* 1297d74d50a8SDag-Erling Smørgrav * fs doesn't support links, so fall back to 1298d74d50a8SDag-Erling Smørgrav * stat+rename. This is racy. 1299d74d50a8SDag-Erling Smørgrav */ 1300d74d50a8SDag-Erling Smørgrav if (stat(newpath, &st) == -1) { 1301d74d50a8SDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1302d74d50a8SDag-Erling Smørgrav status = 1303d74d50a8SDag-Erling Smørgrav errno_to_portable(errno); 1304d74d50a8SDag-Erling Smørgrav else 1305d74d50a8SDag-Erling Smørgrav status = SSH2_FX_OK; 1306d74d50a8SDag-Erling Smørgrav } 1307d74d50a8SDag-Erling Smørgrav } else { 1308d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1309d74d50a8SDag-Erling Smørgrav } 1310d74d50a8SDag-Erling Smørgrav } else if (unlink(oldpath) == -1) { 1311d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1312d0c8c0bcSDag-Erling Smørgrav /* clean spare link */ 1313d0c8c0bcSDag-Erling Smørgrav unlink(newpath); 1314d0c8c0bcSDag-Erling Smørgrav } else 1315d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 1316d0c8c0bcSDag-Erling Smørgrav } else if (stat(newpath, &sb) == -1) { 1317d0c8c0bcSDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1318d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1319d0c8c0bcSDag-Erling Smørgrav else 1320d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 13211e8db6e2SBrian Feldman } 1322b66f2d16SKris Kennaway send_status(id, status); 1323e4a9863fSDag-Erling Smørgrav free(oldpath); 1324e4a9863fSDag-Erling Smørgrav free(newpath); 1325b66f2d16SKris Kennaway } 1326b66f2d16SKris Kennaway 1327ae1f160dSDag-Erling Smørgrav static void 1328f7167e0eSDag-Erling Smørgrav process_readlink(u_int32_t id) 13291e8db6e2SBrian Feldman { 1330bc5531deSDag-Erling Smørgrav int r, len; 1331bc5531deSDag-Erling Smørgrav char buf[PATH_MAX]; 13321e8db6e2SBrian Feldman char *path; 13331e8db6e2SBrian Feldman 1334bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 133519261079SEd Maste fatal_fr(r, "parse"); 1336bc5531deSDag-Erling Smørgrav 1337761efaa7SDag-Erling Smørgrav debug3("request %u: readlink", id); 1338761efaa7SDag-Erling Smørgrav verbose("readlink \"%s\"", path); 1339d74d50a8SDag-Erling Smørgrav if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) 13401e8db6e2SBrian Feldman send_status(id, errno_to_portable(errno)); 13411e8db6e2SBrian Feldman else { 13421e8db6e2SBrian Feldman Stat s; 13431e8db6e2SBrian Feldman 1344d74d50a8SDag-Erling Smørgrav buf[len] = '\0'; 13451e8db6e2SBrian Feldman attrib_clear(&s.attrib); 1346d74d50a8SDag-Erling Smørgrav s.name = s.long_name = buf; 13471e8db6e2SBrian Feldman send_names(id, 1, &s); 13481e8db6e2SBrian Feldman } 1349e4a9863fSDag-Erling Smørgrav free(path); 13501e8db6e2SBrian Feldman } 13511e8db6e2SBrian Feldman 1352ae1f160dSDag-Erling Smørgrav static void 1353f7167e0eSDag-Erling Smørgrav process_symlink(u_int32_t id) 13541e8db6e2SBrian Feldman { 13551e8db6e2SBrian Feldman char *oldpath, *newpath; 1356bc5531deSDag-Erling Smørgrav int r, status; 13571e8db6e2SBrian Feldman 1358bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1359bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 136019261079SEd Maste fatal_fr(r, "parse"); 1361bc5531deSDag-Erling Smørgrav 1362761efaa7SDag-Erling Smørgrav debug3("request %u: symlink", id); 1363761efaa7SDag-Erling Smørgrav logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); 1364d0c8c0bcSDag-Erling Smørgrav /* this will fail if 'newpath' exists */ 1365bc5531deSDag-Erling Smørgrav r = symlink(oldpath, newpath); 1366bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 13671e8db6e2SBrian Feldman send_status(id, status); 1368e4a9863fSDag-Erling Smørgrav free(oldpath); 1369e4a9863fSDag-Erling Smørgrav free(newpath); 13701e8db6e2SBrian Feldman } 13711e8db6e2SBrian Feldman 1372ae1f160dSDag-Erling Smørgrav static void 1373d4af9e69SDag-Erling Smørgrav process_extended_posix_rename(u_int32_t id) 1374d4af9e69SDag-Erling Smørgrav { 1375d4af9e69SDag-Erling Smørgrav char *oldpath, *newpath; 1376bc5531deSDag-Erling Smørgrav int r, status; 1377d4af9e69SDag-Erling Smørgrav 1378bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1379bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 138019261079SEd Maste fatal_fr(r, "parse"); 1381bc5531deSDag-Erling Smørgrav 1382d4af9e69SDag-Erling Smørgrav debug3("request %u: posix-rename", id); 1383d4af9e69SDag-Erling Smørgrav logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); 1384bc5531deSDag-Erling Smørgrav r = rename(oldpath, newpath); 1385bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1386b15c8340SDag-Erling Smørgrav send_status(id, status); 1387e4a9863fSDag-Erling Smørgrav free(oldpath); 1388e4a9863fSDag-Erling Smørgrav free(newpath); 1389d4af9e69SDag-Erling Smørgrav } 1390d4af9e69SDag-Erling Smørgrav 1391d4af9e69SDag-Erling Smørgrav static void 1392d4af9e69SDag-Erling Smørgrav process_extended_statvfs(u_int32_t id) 1393d4af9e69SDag-Erling Smørgrav { 1394d4af9e69SDag-Erling Smørgrav char *path; 1395d4af9e69SDag-Erling Smørgrav struct statvfs st; 1396bc5531deSDag-Erling Smørgrav int r; 1397d4af9e69SDag-Erling Smørgrav 1398bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 139919261079SEd Maste fatal_fr(r, "parse"); 1400f7167e0eSDag-Erling Smørgrav debug3("request %u: statvfs", id); 1401f7167e0eSDag-Erling Smørgrav logit("statvfs \"%s\"", path); 1402d4af9e69SDag-Erling Smørgrav 1403d4af9e69SDag-Erling Smørgrav if (statvfs(path, &st) != 0) 1404d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1405d4af9e69SDag-Erling Smørgrav else 1406d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st); 1407e4a9863fSDag-Erling Smørgrav free(path); 1408d4af9e69SDag-Erling Smørgrav } 1409d4af9e69SDag-Erling Smørgrav 1410d4af9e69SDag-Erling Smørgrav static void 1411d4af9e69SDag-Erling Smørgrav process_extended_fstatvfs(u_int32_t id) 1412d4af9e69SDag-Erling Smørgrav { 1413bc5531deSDag-Erling Smørgrav int r, handle, fd; 1414d4af9e69SDag-Erling Smørgrav struct statvfs st; 1415d4af9e69SDag-Erling Smørgrav 1416bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 141719261079SEd Maste fatal_fr(r, "parse"); 1418d4af9e69SDag-Erling Smørgrav debug("request %u: fstatvfs \"%s\" (handle %u)", 1419d4af9e69SDag-Erling Smørgrav id, handle_to_name(handle), handle); 1420d4af9e69SDag-Erling Smørgrav if ((fd = handle_to_fd(handle)) < 0) { 1421d4af9e69SDag-Erling Smørgrav send_status(id, SSH2_FX_FAILURE); 1422d4af9e69SDag-Erling Smørgrav return; 1423d4af9e69SDag-Erling Smørgrav } 1424d4af9e69SDag-Erling Smørgrav if (fstatvfs(fd, &st) != 0) 1425d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1426d4af9e69SDag-Erling Smørgrav else 1427d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st); 1428d4af9e69SDag-Erling Smørgrav } 1429d4af9e69SDag-Erling Smørgrav 1430d4af9e69SDag-Erling Smørgrav static void 14314a421b63SDag-Erling Smørgrav process_extended_hardlink(u_int32_t id) 14324a421b63SDag-Erling Smørgrav { 14334a421b63SDag-Erling Smørgrav char *oldpath, *newpath; 1434bc5531deSDag-Erling Smørgrav int r, status; 14354a421b63SDag-Erling Smørgrav 1436bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1437bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 143819261079SEd Maste fatal_fr(r, "parse"); 1439bc5531deSDag-Erling Smørgrav 14404a421b63SDag-Erling Smørgrav debug3("request %u: hardlink", id); 14414a421b63SDag-Erling Smørgrav logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath); 1442bc5531deSDag-Erling Smørgrav r = link(oldpath, newpath); 1443bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 14444a421b63SDag-Erling Smørgrav send_status(id, status); 1445e4a9863fSDag-Erling Smørgrav free(oldpath); 1446e4a9863fSDag-Erling Smørgrav free(newpath); 14474a421b63SDag-Erling Smørgrav } 14484a421b63SDag-Erling Smørgrav 14494a421b63SDag-Erling Smørgrav static void 1450f7167e0eSDag-Erling Smørgrav process_extended_fsync(u_int32_t id) 14511e8db6e2SBrian Feldman { 1452bc5531deSDag-Erling Smørgrav int handle, fd, r, status = SSH2_FX_OP_UNSUPPORTED; 14531e8db6e2SBrian Feldman 1454bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 145519261079SEd Maste fatal_fr(r, "parse"); 1456f7167e0eSDag-Erling Smørgrav debug3("request %u: fsync (handle %u)", id, handle); 1457f7167e0eSDag-Erling Smørgrav verbose("fsync \"%s\"", handle_to_name(handle)); 1458f7167e0eSDag-Erling Smørgrav if ((fd = handle_to_fd(handle)) < 0) 1459f7167e0eSDag-Erling Smørgrav status = SSH2_FX_NO_SUCH_FILE; 1460f7167e0eSDag-Erling Smørgrav else if (handle_is_ok(handle, HANDLE_FILE)) { 1461bc5531deSDag-Erling Smørgrav r = fsync(fd); 1462bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1463f7167e0eSDag-Erling Smørgrav } 1464f7167e0eSDag-Erling Smørgrav send_status(id, status); 1465f7167e0eSDag-Erling Smørgrav } 1466f7167e0eSDag-Erling Smørgrav 1467f7167e0eSDag-Erling Smørgrav static void 146819261079SEd Maste process_extended_lsetstat(u_int32_t id) 146919261079SEd Maste { 147019261079SEd Maste Attrib a; 147119261079SEd Maste char *name; 147219261079SEd Maste int r, status = SSH2_FX_OK; 147319261079SEd Maste 147419261079SEd Maste if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 147519261079SEd Maste (r = decode_attrib(iqueue, &a)) != 0) 147619261079SEd Maste fatal_fr(r, "parse"); 147719261079SEd Maste 147819261079SEd Maste debug("request %u: lsetstat name \"%s\"", id, name); 147919261079SEd Maste if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { 148019261079SEd Maste /* nonsensical for links */ 148119261079SEd Maste status = SSH2_FX_BAD_MESSAGE; 148219261079SEd Maste goto out; 148319261079SEd Maste } 148419261079SEd Maste if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 148519261079SEd Maste logit("set \"%s\" mode %04o", name, a.perm); 148619261079SEd Maste r = fchmodat(AT_FDCWD, name, 148719261079SEd Maste a.perm & 07777, AT_SYMLINK_NOFOLLOW); 148819261079SEd Maste if (r == -1) 148919261079SEd Maste status = errno_to_portable(errno); 149019261079SEd Maste } 149119261079SEd Maste if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 149219261079SEd Maste char buf[64]; 149319261079SEd Maste time_t t = a.mtime; 149419261079SEd Maste 149519261079SEd Maste strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 149619261079SEd Maste localtime(&t)); 149719261079SEd Maste logit("set \"%s\" modtime %s", name, buf); 149819261079SEd Maste r = utimensat(AT_FDCWD, name, 149919261079SEd Maste attrib_to_ts(&a), AT_SYMLINK_NOFOLLOW); 150019261079SEd Maste if (r == -1) 150119261079SEd Maste status = errno_to_portable(errno); 150219261079SEd Maste } 150319261079SEd Maste if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { 150419261079SEd Maste logit("set \"%s\" owner %lu group %lu", name, 150519261079SEd Maste (u_long)a.uid, (u_long)a.gid); 150619261079SEd Maste r = fchownat(AT_FDCWD, name, a.uid, a.gid, 150719261079SEd Maste AT_SYMLINK_NOFOLLOW); 150819261079SEd Maste if (r == -1) 150919261079SEd Maste status = errno_to_portable(errno); 151019261079SEd Maste } 151119261079SEd Maste out: 151219261079SEd Maste send_status(id, status); 151319261079SEd Maste free(name); 151419261079SEd Maste } 151519261079SEd Maste 151619261079SEd Maste static void 151719261079SEd Maste process_extended_limits(u_int32_t id) 151819261079SEd Maste { 151919261079SEd Maste struct sshbuf *msg; 152019261079SEd Maste int r; 152119261079SEd Maste uint64_t nfiles = 0; 152219261079SEd Maste #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) 152319261079SEd Maste struct rlimit rlim; 152419261079SEd Maste #endif 152519261079SEd Maste 152619261079SEd Maste debug("request %u: limits", id); 152719261079SEd Maste 152819261079SEd Maste #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) 152919261079SEd Maste if (getrlimit(RLIMIT_NOFILE, &rlim) != -1 && rlim.rlim_cur > 5) 153019261079SEd Maste nfiles = rlim.rlim_cur - 5; /* stdio(3) + syslog + spare */ 153119261079SEd Maste #endif 153219261079SEd Maste 153319261079SEd Maste if ((msg = sshbuf_new()) == NULL) 153419261079SEd Maste fatal_f("sshbuf_new failed"); 153519261079SEd Maste if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED_REPLY)) != 0 || 153619261079SEd Maste (r = sshbuf_put_u32(msg, id)) != 0 || 153719261079SEd Maste /* max-packet-length */ 153819261079SEd Maste (r = sshbuf_put_u64(msg, SFTP_MAX_MSG_LENGTH)) != 0 || 153919261079SEd Maste /* max-read-length */ 154019261079SEd Maste (r = sshbuf_put_u64(msg, SFTP_MAX_READ_LENGTH)) != 0 || 154119261079SEd Maste /* max-write-length */ 154219261079SEd Maste (r = sshbuf_put_u64(msg, SFTP_MAX_MSG_LENGTH - 1024)) != 0 || 154319261079SEd Maste /* max-open-handles */ 154419261079SEd Maste (r = sshbuf_put_u64(msg, nfiles)) != 0) 154519261079SEd Maste fatal_fr(r, "compose"); 154619261079SEd Maste send_msg(msg); 154719261079SEd Maste sshbuf_free(msg); 154819261079SEd Maste } 154919261079SEd Maste 155019261079SEd Maste static void 155119261079SEd Maste process_extended_expand(u_int32_t id) 155219261079SEd Maste { 155319261079SEd Maste char cwd[PATH_MAX], resolvedname[PATH_MAX]; 155419261079SEd Maste char *path, *npath; 155519261079SEd Maste int r; 155619261079SEd Maste Stat s; 155719261079SEd Maste 155819261079SEd Maste if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 155919261079SEd Maste fatal_fr(r, "parse"); 156019261079SEd Maste if (getcwd(cwd, sizeof(cwd)) == NULL) { 156119261079SEd Maste send_status(id, errno_to_portable(errno)); 156219261079SEd Maste goto out; 156319261079SEd Maste } 156419261079SEd Maste 156519261079SEd Maste debug3("request %u: expand, original \"%s\"", id, path); 156619261079SEd Maste if (path[0] == '\0') { 156719261079SEd Maste /* empty path */ 156819261079SEd Maste free(path); 156919261079SEd Maste path = xstrdup("."); 157019261079SEd Maste } else if (*path == '~') { 157119261079SEd Maste /* ~ expand path */ 157219261079SEd Maste /* Special-case for "~" and "~/" to respect homedir flag */ 157319261079SEd Maste if (strcmp(path, "~") == 0) { 157419261079SEd Maste free(path); 157519261079SEd Maste path = xstrdup(cwd); 157619261079SEd Maste } else if (strncmp(path, "~/", 2) == 0) { 157719261079SEd Maste npath = xstrdup(path + 2); 157819261079SEd Maste free(path); 157919261079SEd Maste xasprintf(&path, "%s/%s", cwd, npath); 15801323ec57SEd Maste free(npath); 158119261079SEd Maste } else { 158219261079SEd Maste /* ~user expansions */ 158319261079SEd Maste if (tilde_expand(path, pw->pw_uid, &npath) != 0) { 15841323ec57SEd Maste send_status_errmsg(id, 15851323ec57SEd Maste errno_to_portable(ENOENT), "no such user"); 158619261079SEd Maste goto out; 158719261079SEd Maste } 158819261079SEd Maste free(path); 158919261079SEd Maste path = npath; 159019261079SEd Maste } 159119261079SEd Maste } else if (*path != '/') { 159219261079SEd Maste /* relative path */ 159319261079SEd Maste xasprintf(&npath, "%s/%s", cwd, path); 159419261079SEd Maste free(path); 159519261079SEd Maste path = npath; 159619261079SEd Maste } 159719261079SEd Maste verbose("expand \"%s\"", path); 159819261079SEd Maste if (sftp_realpath(path, resolvedname) == NULL) { 159919261079SEd Maste send_status(id, errno_to_portable(errno)); 160019261079SEd Maste goto out; 160119261079SEd Maste } 160219261079SEd Maste attrib_clear(&s.attrib); 160319261079SEd Maste s.name = s.long_name = resolvedname; 160419261079SEd Maste send_names(id, 1, &s); 160519261079SEd Maste out: 160619261079SEd Maste free(path); 160719261079SEd Maste } 160819261079SEd Maste 160919261079SEd Maste static void 161087c1498dSEd Maste process_extended_copy_data(u_int32_t id) 161187c1498dSEd Maste { 161287c1498dSEd Maste u_char buf[64*1024]; 161387c1498dSEd Maste int read_handle, read_fd, write_handle, write_fd; 161487c1498dSEd Maste u_int64_t len, read_off, read_len, write_off; 161587c1498dSEd Maste int r, copy_until_eof, status = SSH2_FX_OP_UNSUPPORTED; 161687c1498dSEd Maste size_t ret; 161787c1498dSEd Maste 161887c1498dSEd Maste if ((r = get_handle(iqueue, &read_handle)) != 0 || 161987c1498dSEd Maste (r = sshbuf_get_u64(iqueue, &read_off)) != 0 || 162087c1498dSEd Maste (r = sshbuf_get_u64(iqueue, &read_len)) != 0 || 162187c1498dSEd Maste (r = get_handle(iqueue, &write_handle)) != 0 || 162287c1498dSEd Maste (r = sshbuf_get_u64(iqueue, &write_off)) != 0) 162387c1498dSEd Maste fatal("%s: buffer error: %s", __func__, ssh_err(r)); 162487c1498dSEd Maste 162587c1498dSEd Maste debug("request %u: copy-data from \"%s\" (handle %d) off %llu len %llu " 162687c1498dSEd Maste "to \"%s\" (handle %d) off %llu", 162787c1498dSEd Maste id, handle_to_name(read_handle), read_handle, 162887c1498dSEd Maste (unsigned long long)read_off, (unsigned long long)read_len, 162987c1498dSEd Maste handle_to_name(write_handle), write_handle, 163087c1498dSEd Maste (unsigned long long)write_off); 163187c1498dSEd Maste 163287c1498dSEd Maste /* For read length of 0, we read until EOF. */ 163387c1498dSEd Maste if (read_len == 0) { 163487c1498dSEd Maste read_len = (u_int64_t)-1 - read_off; 163587c1498dSEd Maste copy_until_eof = 1; 163687c1498dSEd Maste } else 163787c1498dSEd Maste copy_until_eof = 0; 163887c1498dSEd Maste 163987c1498dSEd Maste read_fd = handle_to_fd(read_handle); 164087c1498dSEd Maste write_fd = handle_to_fd(write_handle); 164187c1498dSEd Maste 164287c1498dSEd Maste /* Disallow reading & writing to the same handle or same path or dirs */ 164387c1498dSEd Maste if (read_handle == write_handle || read_fd < 0 || write_fd < 0 || 164487c1498dSEd Maste !strcmp(handle_to_name(read_handle), handle_to_name(write_handle))) { 164587c1498dSEd Maste status = SSH2_FX_FAILURE; 164687c1498dSEd Maste goto out; 164787c1498dSEd Maste } 164887c1498dSEd Maste 164987c1498dSEd Maste if (lseek(read_fd, read_off, SEEK_SET) < 0) { 165087c1498dSEd Maste status = errno_to_portable(errno); 165187c1498dSEd Maste error("%s: read_seek failed", __func__); 165287c1498dSEd Maste goto out; 165387c1498dSEd Maste } 165487c1498dSEd Maste 165587c1498dSEd Maste if ((handle_to_flags(write_handle) & O_APPEND) == 0 && 165687c1498dSEd Maste lseek(write_fd, write_off, SEEK_SET) < 0) { 165787c1498dSEd Maste status = errno_to_portable(errno); 165887c1498dSEd Maste error("%s: write_seek failed", __func__); 165987c1498dSEd Maste goto out; 166087c1498dSEd Maste } 166187c1498dSEd Maste 166287c1498dSEd Maste /* Process the request in chunks. */ 166387c1498dSEd Maste while (read_len > 0 || copy_until_eof) { 166487c1498dSEd Maste len = MINIMUM(sizeof(buf), read_len); 166587c1498dSEd Maste read_len -= len; 166687c1498dSEd Maste 166787c1498dSEd Maste ret = atomicio(read, read_fd, buf, len); 166887c1498dSEd Maste if (ret == 0 && errno == EPIPE) { 166987c1498dSEd Maste status = copy_until_eof ? SSH2_FX_OK : SSH2_FX_EOF; 167087c1498dSEd Maste break; 167187c1498dSEd Maste } else if (ret == 0) { 167287c1498dSEd Maste status = errno_to_portable(errno); 167387c1498dSEd Maste error("%s: read failed: %s", __func__, strerror(errno)); 167487c1498dSEd Maste break; 167587c1498dSEd Maste } 167687c1498dSEd Maste len = ret; 167787c1498dSEd Maste handle_update_read(read_handle, len); 167887c1498dSEd Maste 167987c1498dSEd Maste ret = atomicio(vwrite, write_fd, buf, len); 168087c1498dSEd Maste if (ret != len) { 168187c1498dSEd Maste status = errno_to_portable(errno); 168287c1498dSEd Maste error("%s: write failed: %llu != %llu: %s", __func__, 168387c1498dSEd Maste (unsigned long long)ret, (unsigned long long)len, 168487c1498dSEd Maste strerror(errno)); 168587c1498dSEd Maste break; 168687c1498dSEd Maste } 168787c1498dSEd Maste handle_update_write(write_handle, len); 168887c1498dSEd Maste } 168987c1498dSEd Maste 169087c1498dSEd Maste if (read_len == 0) 169187c1498dSEd Maste status = SSH2_FX_OK; 169287c1498dSEd Maste 169387c1498dSEd Maste out: 169487c1498dSEd Maste send_status(id, status); 169587c1498dSEd Maste } 169687c1498dSEd Maste 169787c1498dSEd Maste static void 169838a52bd3SEd Maste process_extended_home_directory(u_int32_t id) 169938a52bd3SEd Maste { 170038a52bd3SEd Maste char *username; 170138a52bd3SEd Maste struct passwd *user_pw; 170238a52bd3SEd Maste int r; 170338a52bd3SEd Maste Stat s; 170438a52bd3SEd Maste 170538a52bd3SEd Maste if ((r = sshbuf_get_cstring(iqueue, &username, NULL)) != 0) 170638a52bd3SEd Maste fatal_fr(r, "parse"); 170738a52bd3SEd Maste 170838a52bd3SEd Maste debug3("request %u: home-directory \"%s\"", id, username); 170938a52bd3SEd Maste if ((user_pw = getpwnam(username)) == NULL) { 171038a52bd3SEd Maste send_status(id, SSH2_FX_FAILURE); 171138a52bd3SEd Maste goto out; 171238a52bd3SEd Maste } 171338a52bd3SEd Maste 171438a52bd3SEd Maste verbose("home-directory \"%s\"", pw->pw_dir); 171538a52bd3SEd Maste attrib_clear(&s.attrib); 171638a52bd3SEd Maste s.name = s.long_name = pw->pw_dir; 171738a52bd3SEd Maste send_names(id, 1, &s); 171838a52bd3SEd Maste out: 171938a52bd3SEd Maste free(username); 172038a52bd3SEd Maste } 172138a52bd3SEd Maste 172238a52bd3SEd Maste static void 172338a52bd3SEd Maste process_extended_get_users_groups_by_id(u_int32_t id) 172438a52bd3SEd Maste { 172538a52bd3SEd Maste struct passwd *user_pw; 172638a52bd3SEd Maste struct group *gr; 172738a52bd3SEd Maste struct sshbuf *uids, *gids, *usernames, *groupnames, *msg; 172838a52bd3SEd Maste int r; 172938a52bd3SEd Maste u_int n, nusers = 0, ngroups = 0; 173038a52bd3SEd Maste const char *name; 173138a52bd3SEd Maste 173238a52bd3SEd Maste if ((usernames = sshbuf_new()) == NULL || 173338a52bd3SEd Maste (groupnames = sshbuf_new()) == NULL || 173438a52bd3SEd Maste (msg = sshbuf_new()) == NULL) 173538a52bd3SEd Maste fatal_f("sshbuf_new failed"); 173638a52bd3SEd Maste if ((r = sshbuf_froms(iqueue, &uids)) != 0 || 173738a52bd3SEd Maste (r = sshbuf_froms(iqueue, &gids)) != 0) 173838a52bd3SEd Maste fatal_fr(r, "parse"); 173938a52bd3SEd Maste debug_f("uids len = %zu, gids len = %zu", 174038a52bd3SEd Maste sshbuf_len(uids), sshbuf_len(gids)); 174138a52bd3SEd Maste while (sshbuf_len(uids) != 0) { 174238a52bd3SEd Maste if ((r = sshbuf_get_u32(uids, &n)) != 0) 174338a52bd3SEd Maste fatal_fr(r, "parse inner uid"); 174438a52bd3SEd Maste user_pw = getpwuid((uid_t)n); 174538a52bd3SEd Maste name = user_pw == NULL ? "" : user_pw->pw_name; 174638a52bd3SEd Maste debug3_f("uid %u => \"%s\"", n, name); 174738a52bd3SEd Maste if ((r = sshbuf_put_cstring(usernames, name)) != 0) 1748f374ba41SEd Maste fatal_fr(r, "assemble uid reply"); 174938a52bd3SEd Maste nusers++; 175038a52bd3SEd Maste } 175138a52bd3SEd Maste while (sshbuf_len(gids) != 0) { 175238a52bd3SEd Maste if ((r = sshbuf_get_u32(gids, &n)) != 0) 175338a52bd3SEd Maste fatal_fr(r, "parse inner gid"); 175438a52bd3SEd Maste gr = getgrgid((gid_t)n); 175538a52bd3SEd Maste name = gr == NULL ? "" : gr->gr_name; 175638a52bd3SEd Maste debug3_f("gid %u => \"%s\"", n, name); 175738a52bd3SEd Maste if ((r = sshbuf_put_cstring(groupnames, name)) != 0) 175838a52bd3SEd Maste fatal_fr(r, "assemble gid reply"); 175938a52bd3SEd Maste nusers++; 176038a52bd3SEd Maste } 176138a52bd3SEd Maste verbose("users-groups-by-id: %u users, %u groups", nusers, ngroups); 176238a52bd3SEd Maste 176338a52bd3SEd Maste if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED_REPLY)) != 0 || 176438a52bd3SEd Maste (r = sshbuf_put_u32(msg, id)) != 0 || 176538a52bd3SEd Maste (r = sshbuf_put_stringb(msg, usernames)) != 0 || 176638a52bd3SEd Maste (r = sshbuf_put_stringb(msg, groupnames)) != 0) 176738a52bd3SEd Maste fatal_fr(r, "compose"); 176838a52bd3SEd Maste send_msg(msg); 176938a52bd3SEd Maste 177038a52bd3SEd Maste sshbuf_free(uids); 177138a52bd3SEd Maste sshbuf_free(gids); 177238a52bd3SEd Maste sshbuf_free(usernames); 177338a52bd3SEd Maste sshbuf_free(groupnames); 177438a52bd3SEd Maste sshbuf_free(msg); 177538a52bd3SEd Maste } 177638a52bd3SEd Maste 177738a52bd3SEd Maste static void 1778f7167e0eSDag-Erling Smørgrav process_extended(u_int32_t id) 1779f7167e0eSDag-Erling Smørgrav { 1780f7167e0eSDag-Erling Smørgrav char *request; 178119261079SEd Maste int r; 178219261079SEd Maste const struct sftp_handler *exthand; 1783f7167e0eSDag-Erling Smørgrav 1784bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &request, NULL)) != 0) 178519261079SEd Maste fatal_fr(r, "parse"); 178619261079SEd Maste if ((exthand = extended_handler_byname(request)) == NULL) { 1787f7167e0eSDag-Erling Smørgrav error("Unknown extended request \"%.100s\"", request); 17881e8db6e2SBrian Feldman send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 178919261079SEd Maste } else { 179019261079SEd Maste if (!request_permitted(exthand)) 179119261079SEd Maste send_status(id, SSH2_FX_PERMISSION_DENIED); 179219261079SEd Maste else 179319261079SEd Maste exthand->handler(id); 1794f7167e0eSDag-Erling Smørgrav } 1795e4a9863fSDag-Erling Smørgrav free(request); 17961e8db6e2SBrian Feldman } 1797b66f2d16SKris Kennaway 1798b66f2d16SKris Kennaway /* stolen from ssh-agent */ 1799b66f2d16SKris Kennaway 1800ae1f160dSDag-Erling Smørgrav static void 1801b66f2d16SKris Kennaway process(void) 1802b66f2d16SKris Kennaway { 1803bc5531deSDag-Erling Smørgrav u_int msg_len; 1804bc5531deSDag-Erling Smørgrav u_int buf_len; 1805bc5531deSDag-Erling Smørgrav u_int consumed; 1806bc5531deSDag-Erling Smørgrav u_char type; 1807bc5531deSDag-Erling Smørgrav const u_char *cp; 1808bc5531deSDag-Erling Smørgrav int i, r; 1809f7167e0eSDag-Erling Smørgrav u_int32_t id; 1810b66f2d16SKris Kennaway 1811bc5531deSDag-Erling Smørgrav buf_len = sshbuf_len(iqueue); 1812545d5ecaSDag-Erling Smørgrav if (buf_len < 5) 1813b66f2d16SKris Kennaway return; /* Incomplete message. */ 1814bc5531deSDag-Erling Smørgrav cp = sshbuf_ptr(iqueue); 1815761efaa7SDag-Erling Smørgrav msg_len = get_u32(cp); 1816021d409fSDag-Erling Smørgrav if (msg_len > SFTP_MAX_MSG_LENGTH) { 1817761efaa7SDag-Erling Smørgrav error("bad message from %s local user %s", 1818761efaa7SDag-Erling Smørgrav client_addr, pw->pw_name); 1819d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(11); 1820b66f2d16SKris Kennaway } 1821545d5ecaSDag-Erling Smørgrav if (buf_len < msg_len + 4) 1822b66f2d16SKris Kennaway return; 1823bc5531deSDag-Erling Smørgrav if ((r = sshbuf_consume(iqueue, 4)) != 0) 182419261079SEd Maste fatal_fr(r, "consume"); 1825545d5ecaSDag-Erling Smørgrav buf_len -= 4; 1826bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u8(iqueue, &type)) != 0) 182719261079SEd Maste fatal_fr(r, "parse type"); 1828f7167e0eSDag-Erling Smørgrav 1829b66f2d16SKris Kennaway switch (type) { 18301e8db6e2SBrian Feldman case SSH2_FXP_INIT: 1831b66f2d16SKris Kennaway process_init(); 1832f7167e0eSDag-Erling Smørgrav init_done = 1; 18331e8db6e2SBrian Feldman break; 18341e8db6e2SBrian Feldman case SSH2_FXP_EXTENDED: 1835f7167e0eSDag-Erling Smørgrav if (!init_done) 1836f7167e0eSDag-Erling Smørgrav fatal("Received extended request before init"); 1837bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(iqueue, &id)) != 0) 183819261079SEd Maste fatal_fr(r, "parse extended ID"); 1839f7167e0eSDag-Erling Smørgrav process_extended(id); 18401e8db6e2SBrian Feldman break; 1841b66f2d16SKris Kennaway default: 1842f7167e0eSDag-Erling Smørgrav if (!init_done) 1843f7167e0eSDag-Erling Smørgrav fatal("Received %u request before init", type); 1844bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(iqueue, &id)) != 0) 184519261079SEd Maste fatal_fr(r, "parse ID"); 1846f7167e0eSDag-Erling Smørgrav for (i = 0; handlers[i].handler != NULL; i++) { 1847f7167e0eSDag-Erling Smørgrav if (type == handlers[i].type) { 1848f7167e0eSDag-Erling Smørgrav if (!request_permitted(&handlers[i])) { 1849f7167e0eSDag-Erling Smørgrav send_status(id, 1850f7167e0eSDag-Erling Smørgrav SSH2_FX_PERMISSION_DENIED); 1851f7167e0eSDag-Erling Smørgrav } else { 1852f7167e0eSDag-Erling Smørgrav handlers[i].handler(id); 1853f7167e0eSDag-Erling Smørgrav } 1854b66f2d16SKris Kennaway break; 1855b66f2d16SKris Kennaway } 1856f7167e0eSDag-Erling Smørgrav } 1857f7167e0eSDag-Erling Smørgrav if (handlers[i].handler == NULL) 1858f7167e0eSDag-Erling Smørgrav error("Unknown message %u", type); 1859f7167e0eSDag-Erling Smørgrav } 1860545d5ecaSDag-Erling Smørgrav /* discard the remaining bytes from the current packet */ 1861bc5531deSDag-Erling Smørgrav if (buf_len < sshbuf_len(iqueue)) { 1862d4af9e69SDag-Erling Smørgrav error("iqueue grew unexpectedly"); 1863d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1864d4af9e69SDag-Erling Smørgrav } 1865bc5531deSDag-Erling Smørgrav consumed = buf_len - sshbuf_len(iqueue); 1866d4af9e69SDag-Erling Smørgrav if (msg_len < consumed) { 1867f7167e0eSDag-Erling Smørgrav error("msg_len %u < consumed %u", msg_len, consumed); 1868d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1869d4af9e69SDag-Erling Smørgrav } 1870bc5531deSDag-Erling Smørgrav if (msg_len > consumed && 1871bc5531deSDag-Erling Smørgrav (r = sshbuf_consume(iqueue, msg_len - consumed)) != 0) 187219261079SEd Maste fatal_fr(r, "consume"); 1873b66f2d16SKris Kennaway } 1874b66f2d16SKris Kennaway 1875761efaa7SDag-Erling Smørgrav /* Cleanup handler that logs active handles upon normal exit */ 1876761efaa7SDag-Erling Smørgrav void 1877d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(int i) 1878761efaa7SDag-Erling Smørgrav { 1879761efaa7SDag-Erling Smørgrav if (pw != NULL && client_addr != NULL) { 1880761efaa7SDag-Erling Smørgrav handle_log_exit(); 1881761efaa7SDag-Erling Smørgrav logit("session closed for local user %s from [%s]", 1882761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr); 1883761efaa7SDag-Erling Smørgrav } 1884761efaa7SDag-Erling Smørgrav _exit(i); 1885761efaa7SDag-Erling Smørgrav } 1886761efaa7SDag-Erling Smørgrav 1887761efaa7SDag-Erling Smørgrav static void 1888d4af9e69SDag-Erling Smørgrav sftp_server_usage(void) 1889761efaa7SDag-Erling Smørgrav { 1890761efaa7SDag-Erling Smørgrav extern char *__progname; 1891761efaa7SDag-Erling Smørgrav 1892761efaa7SDag-Erling Smørgrav fprintf(stderr, 18936888a9beSDag-Erling Smørgrav "usage: %s [-ehR] [-d start_directory] [-f log_facility] " 189419261079SEd Maste "[-l log_level]\n\t[-P denied_requests] " 189519261079SEd Maste "[-p allowed_requests] [-u umask]\n" 1896f7167e0eSDag-Erling Smørgrav " %s -Q protocol_feature\n", 1897f7167e0eSDag-Erling Smørgrav __progname, __progname); 1898761efaa7SDag-Erling Smørgrav exit(1); 1899761efaa7SDag-Erling Smørgrav } 1900761efaa7SDag-Erling Smørgrav 1901b66f2d16SKris Kennaway int 1902d4af9e69SDag-Erling Smørgrav sftp_server_main(int argc, char **argv, struct passwd *user_pw) 1903b66f2d16SKris Kennaway { 19041323ec57SEd Maste int i, r, in, out, ch, skipargs = 0, log_stderr = 0; 19051323ec57SEd Maste ssize_t len, olen; 1906761efaa7SDag-Erling Smørgrav SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; 1907190cef3dSDag-Erling Smørgrav char *cp, *homedir = NULL, uidstr[32], buf[4*4096]; 19084a421b63SDag-Erling Smørgrav long mask; 1909761efaa7SDag-Erling Smørgrav 1910761efaa7SDag-Erling Smørgrav extern char *optarg; 1911761efaa7SDag-Erling Smørgrav extern char *__progname; 19121e8db6e2SBrian Feldman 1913761efaa7SDag-Erling Smørgrav __progname = ssh_get_progname(argv[0]); 1914761efaa7SDag-Erling Smørgrav log_init(__progname, log_level, log_facility, log_stderr); 1915b66f2d16SKris Kennaway 19166888a9beSDag-Erling Smørgrav pw = pwcopy(user_pw); 19176888a9beSDag-Erling Smørgrav 1918f7167e0eSDag-Erling Smørgrav while (!skipargs && (ch = getopt(argc, argv, 1919f7167e0eSDag-Erling Smørgrav "d:f:l:P:p:Q:u:cehR")) != -1) { 1920761efaa7SDag-Erling Smørgrav switch (ch) { 1921f7167e0eSDag-Erling Smørgrav case 'Q': 1922f7167e0eSDag-Erling Smørgrav if (strcasecmp(optarg, "requests") != 0) { 1923f7167e0eSDag-Erling Smørgrav fprintf(stderr, "Invalid query type\n"); 1924f7167e0eSDag-Erling Smørgrav exit(1); 1925f7167e0eSDag-Erling Smørgrav } 1926f7167e0eSDag-Erling Smørgrav for (i = 0; handlers[i].handler != NULL; i++) 1927f7167e0eSDag-Erling Smørgrav printf("%s\n", handlers[i].name); 1928f7167e0eSDag-Erling Smørgrav for (i = 0; extended_handlers[i].handler != NULL; i++) 1929f7167e0eSDag-Erling Smørgrav printf("%s\n", extended_handlers[i].name); 1930f7167e0eSDag-Erling Smørgrav exit(0); 1931f7167e0eSDag-Erling Smørgrav break; 1932b15c8340SDag-Erling Smørgrav case 'R': 1933b15c8340SDag-Erling Smørgrav readonly = 1; 1934b15c8340SDag-Erling Smørgrav break; 1935761efaa7SDag-Erling Smørgrav case 'c': 1936761efaa7SDag-Erling Smørgrav /* 1937761efaa7SDag-Erling Smørgrav * Ignore all arguments if we are invoked as a 1938761efaa7SDag-Erling Smørgrav * shell using "sftp-server -c command" 1939761efaa7SDag-Erling Smørgrav */ 1940761efaa7SDag-Erling Smørgrav skipargs = 1; 1941761efaa7SDag-Erling Smørgrav break; 1942761efaa7SDag-Erling Smørgrav case 'e': 1943761efaa7SDag-Erling Smørgrav log_stderr = 1; 1944761efaa7SDag-Erling Smørgrav break; 1945761efaa7SDag-Erling Smørgrav case 'l': 1946761efaa7SDag-Erling Smørgrav log_level = log_level_number(optarg); 1947761efaa7SDag-Erling Smørgrav if (log_level == SYSLOG_LEVEL_NOT_SET) 1948761efaa7SDag-Erling Smørgrav error("Invalid log level \"%s\"", optarg); 1949761efaa7SDag-Erling Smørgrav break; 1950761efaa7SDag-Erling Smørgrav case 'f': 1951761efaa7SDag-Erling Smørgrav log_facility = log_facility_number(optarg); 1952d4af9e69SDag-Erling Smørgrav if (log_facility == SYSLOG_FACILITY_NOT_SET) 1953761efaa7SDag-Erling Smørgrav error("Invalid log facility \"%s\"", optarg); 1954761efaa7SDag-Erling Smørgrav break; 19556888a9beSDag-Erling Smørgrav case 'd': 19566888a9beSDag-Erling Smørgrav cp = tilde_expand_filename(optarg, user_pw->pw_uid); 1957190cef3dSDag-Erling Smørgrav snprintf(uidstr, sizeof(uidstr), "%llu", 1958190cef3dSDag-Erling Smørgrav (unsigned long long)pw->pw_uid); 19596888a9beSDag-Erling Smørgrav homedir = percent_expand(cp, "d", user_pw->pw_dir, 1960190cef3dSDag-Erling Smørgrav "u", user_pw->pw_name, "U", uidstr, (char *)NULL); 19616888a9beSDag-Erling Smørgrav free(cp); 19626888a9beSDag-Erling Smørgrav break; 1963f7167e0eSDag-Erling Smørgrav case 'p': 196419261079SEd Maste if (request_allowlist != NULL) 1965f7167e0eSDag-Erling Smørgrav fatal("Permitted requests already set"); 196619261079SEd Maste request_allowlist = xstrdup(optarg); 1967f7167e0eSDag-Erling Smørgrav break; 1968f7167e0eSDag-Erling Smørgrav case 'P': 196919261079SEd Maste if (request_denylist != NULL) 1970f7167e0eSDag-Erling Smørgrav fatal("Refused requests already set"); 197119261079SEd Maste request_denylist = xstrdup(optarg); 1972f7167e0eSDag-Erling Smørgrav break; 1973b15c8340SDag-Erling Smørgrav case 'u': 19744a421b63SDag-Erling Smørgrav errno = 0; 19754a421b63SDag-Erling Smørgrav mask = strtol(optarg, &cp, 8); 19764a421b63SDag-Erling Smørgrav if (mask < 0 || mask > 0777 || *cp != '\0' || 19774a421b63SDag-Erling Smørgrav cp == optarg || (mask == 0 && errno != 0)) 19784a421b63SDag-Erling Smørgrav fatal("Invalid umask \"%s\"", optarg); 19794a421b63SDag-Erling Smørgrav (void)umask((mode_t)mask); 1980b15c8340SDag-Erling Smørgrav break; 1981761efaa7SDag-Erling Smørgrav case 'h': 1982761efaa7SDag-Erling Smørgrav default: 1983d4af9e69SDag-Erling Smørgrav sftp_server_usage(); 1984761efaa7SDag-Erling Smørgrav } 1985761efaa7SDag-Erling Smørgrav } 1986761efaa7SDag-Erling Smørgrav 1987761efaa7SDag-Erling Smørgrav log_init(__progname, log_level, log_facility, log_stderr); 1988761efaa7SDag-Erling Smørgrav 1989a0ee8cc6SDag-Erling Smørgrav /* 1990076ad2f8SDag-Erling Smørgrav * On platforms where we can, avoid making /proc/self/{mem,maps} 1991a0ee8cc6SDag-Erling Smørgrav * available to the user so that sftp access doesn't automatically 1992a0ee8cc6SDag-Erling Smørgrav * imply arbitrary code execution access that will break 1993a0ee8cc6SDag-Erling Smørgrav * restricted configurations. 1994a0ee8cc6SDag-Erling Smørgrav */ 1995076ad2f8SDag-Erling Smørgrav platform_disable_tracing(1); /* strict */ 1996a0ee8cc6SDag-Erling Smørgrav 1997acc1a9efSDag-Erling Smørgrav /* Drop any fine-grained privileges we don't need */ 1998acc1a9efSDag-Erling Smørgrav platform_pledge_sftp_server(); 1999acc1a9efSDag-Erling Smørgrav 2000761efaa7SDag-Erling Smørgrav if ((cp = getenv("SSH_CONNECTION")) != NULL) { 2001761efaa7SDag-Erling Smørgrav client_addr = xstrdup(cp); 2002d4af9e69SDag-Erling Smørgrav if ((cp = strchr(client_addr, ' ')) == NULL) { 2003d4af9e69SDag-Erling Smørgrav error("Malformed SSH_CONNECTION variable: \"%s\"", 2004761efaa7SDag-Erling Smørgrav getenv("SSH_CONNECTION")); 2005d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 2006d4af9e69SDag-Erling Smørgrav } 2007761efaa7SDag-Erling Smørgrav *cp = '\0'; 2008761efaa7SDag-Erling Smørgrav } else 2009761efaa7SDag-Erling Smørgrav client_addr = xstrdup("UNKNOWN"); 2010761efaa7SDag-Erling Smørgrav 2011761efaa7SDag-Erling Smørgrav logit("session opened for local user %s from [%s]", 2012761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr); 2013761efaa7SDag-Erling Smørgrav 2014b15c8340SDag-Erling Smørgrav in = STDIN_FILENO; 2015b15c8340SDag-Erling Smørgrav out = STDOUT_FILENO; 2016b66f2d16SKris Kennaway 201783d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN 201883d2307dSDag-Erling Smørgrav setmode(in, O_BINARY); 201983d2307dSDag-Erling Smørgrav setmode(out, O_BINARY); 202083d2307dSDag-Erling Smørgrav #endif 202183d2307dSDag-Erling Smørgrav 2022bc5531deSDag-Erling Smørgrav if ((iqueue = sshbuf_new()) == NULL) 202319261079SEd Maste fatal_f("sshbuf_new failed"); 2024bc5531deSDag-Erling Smørgrav if ((oqueue = sshbuf_new()) == NULL) 202519261079SEd Maste fatal_f("sshbuf_new failed"); 2026b66f2d16SKris Kennaway 20276888a9beSDag-Erling Smørgrav if (homedir != NULL) { 20286888a9beSDag-Erling Smørgrav if (chdir(homedir) != 0) { 20296888a9beSDag-Erling Smørgrav error("chdir to \"%s\" failed: %s", homedir, 20306888a9beSDag-Erling Smørgrav strerror(errno)); 20316888a9beSDag-Erling Smørgrav } 20326888a9beSDag-Erling Smørgrav } 20336888a9beSDag-Erling Smørgrav 20341e8db6e2SBrian Feldman for (;;) { 20351323ec57SEd Maste struct pollfd pfd[2]; 20361323ec57SEd Maste 20371323ec57SEd Maste memset(pfd, 0, sizeof pfd); 20381323ec57SEd Maste pfd[0].fd = pfd[1].fd = -1; 20391e8db6e2SBrian Feldman 2040d4af9e69SDag-Erling Smørgrav /* 2041d4af9e69SDag-Erling Smørgrav * Ensure that we can read a full buffer and handle 2042d4af9e69SDag-Erling Smørgrav * the worst-case length packet it can generate, 2043d4af9e69SDag-Erling Smørgrav * otherwise apply backpressure by stopping reads. 2044d4af9e69SDag-Erling Smørgrav */ 2045bc5531deSDag-Erling Smørgrav if ((r = sshbuf_check_reserve(iqueue, sizeof(buf))) == 0 && 2046bc5531deSDag-Erling Smørgrav (r = sshbuf_check_reserve(oqueue, 20471323ec57SEd Maste SFTP_MAX_MSG_LENGTH)) == 0) { 20481323ec57SEd Maste pfd[0].fd = in; 20491323ec57SEd Maste pfd[0].events = POLLIN; 20501323ec57SEd Maste } 2051bc5531deSDag-Erling Smørgrav else if (r != SSH_ERR_NO_BUFFER_SPACE) 205219261079SEd Maste fatal_fr(r, "reserve"); 2053d4af9e69SDag-Erling Smørgrav 2054bc5531deSDag-Erling Smørgrav olen = sshbuf_len(oqueue); 20551323ec57SEd Maste if (olen > 0) { 20561323ec57SEd Maste pfd[1].fd = out; 20571323ec57SEd Maste pfd[1].events = POLLOUT; 20581323ec57SEd Maste } 2059b66f2d16SKris Kennaway 20601323ec57SEd Maste if (poll(pfd, 2, -1) == -1) { 2061b66f2d16SKris Kennaway if (errno == EINTR) 2062b66f2d16SKris Kennaway continue; 20631323ec57SEd Maste error("poll: %s", strerror(errno)); 2064d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(2); 2065b66f2d16SKris Kennaway } 2066b66f2d16SKris Kennaway 2067b66f2d16SKris Kennaway /* copy stdin to iqueue */ 20681323ec57SEd Maste if (pfd[0].revents & (POLLIN|POLLHUP)) { 2069b66f2d16SKris Kennaway len = read(in, buf, sizeof buf); 2070b66f2d16SKris Kennaway if (len == 0) { 2071b66f2d16SKris Kennaway debug("read eof"); 2072d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(0); 207319261079SEd Maste } else if (len == -1) { 20741323ec57SEd Maste if (errno != EAGAIN && errno != EINTR) { 2075761efaa7SDag-Erling Smørgrav error("read: %s", strerror(errno)); 2076d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1); 20771323ec57SEd Maste } 207819261079SEd Maste } else if ((r = sshbuf_put(iqueue, buf, len)) != 0) 207919261079SEd Maste fatal_fr(r, "sshbuf_put"); 2080b66f2d16SKris Kennaway } 2081b66f2d16SKris Kennaway /* send oqueue to stdout */ 20821323ec57SEd Maste if (pfd[1].revents & (POLLOUT|POLLHUP)) { 2083bc5531deSDag-Erling Smørgrav len = write(out, sshbuf_ptr(oqueue), olen); 20841323ec57SEd Maste if (len == 0 || (len == -1 && errno == EPIPE)) { 20851323ec57SEd Maste debug("write eof"); 20861323ec57SEd Maste sftp_server_cleanup_exit(0); 20871323ec57SEd Maste } else if (len == -1) { 20881323ec57SEd Maste sftp_server_cleanup_exit(1); 20891323ec57SEd Maste if (errno != EAGAIN && errno != EINTR) { 2090761efaa7SDag-Erling Smørgrav error("write: %s", strerror(errno)); 2091d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1); 20921323ec57SEd Maste } 209319261079SEd Maste } else if ((r = sshbuf_consume(oqueue, len)) != 0) 209419261079SEd Maste fatal_fr(r, "consume"); 2095b66f2d16SKris Kennaway } 2096d4af9e69SDag-Erling Smørgrav 2097d4af9e69SDag-Erling Smørgrav /* 2098d4af9e69SDag-Erling Smørgrav * Process requests from client if we can fit the results 2099d4af9e69SDag-Erling Smørgrav * into the output buffer, otherwise stop processing input 2100d4af9e69SDag-Erling Smørgrav * and let the output queue drain. 2101d4af9e69SDag-Erling Smørgrav */ 2102bc5531deSDag-Erling Smørgrav r = sshbuf_check_reserve(oqueue, SFTP_MAX_MSG_LENGTH); 2103bc5531deSDag-Erling Smørgrav if (r == 0) 2104b66f2d16SKris Kennaway process(); 2105bc5531deSDag-Erling Smørgrav else if (r != SSH_ERR_NO_BUFFER_SPACE) 210619261079SEd Maste fatal_fr(r, "reserve"); 2107b66f2d16SKris Kennaway } 2108b66f2d16SKris Kennaway } 2109