1*1323ec57SEd Maste /* $OpenBSD: sftp-server.c,v 1.139 2022/02/01 23:32:51 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> 36*1323ec57SEd Maste #ifdef HAVE_POLL_H 37*1323ec57SEd Maste #include <poll.h> 38*1323ec57SEd Maste #endif 39761efaa7SDag-Erling Smørgrav #include <pwd.h> 40761efaa7SDag-Erling Smørgrav #include <stdlib.h> 41761efaa7SDag-Erling Smørgrav #include <stdio.h> 42761efaa7SDag-Erling Smørgrav #include <string.h> 43761efaa7SDag-Erling Smørgrav #include <time.h> 44761efaa7SDag-Erling Smørgrav #include <unistd.h> 45761efaa7SDag-Erling Smørgrav #include <stdarg.h> 46761efaa7SDag-Erling Smørgrav 47b66f2d16SKris Kennaway #include "xmalloc.h" 48bc5531deSDag-Erling Smørgrav #include "sshbuf.h" 49bc5531deSDag-Erling Smørgrav #include "ssherr.h" 50761efaa7SDag-Erling Smørgrav #include "log.h" 51021d409fSDag-Erling Smørgrav #include "misc.h" 52f7167e0eSDag-Erling Smørgrav #include "match.h" 53761efaa7SDag-Erling Smørgrav #include "uidswap.h" 54b66f2d16SKris Kennaway 551e8db6e2SBrian Feldman #include "sftp.h" 561e8db6e2SBrian Feldman #include "sftp-common.h" 57b66f2d16SKris Kennaway 5819261079SEd Maste char *sftp_realpath(const char *, char *); /* sftp-realpath.c */ 5919261079SEd Maste 6019261079SEd Maste /* Maximum data read that we are willing to accept */ 6119261079SEd Maste #define SFTP_MAX_READ_LENGTH (SFTP_MAX_MSG_LENGTH - 1024) 6219261079SEd Maste 63761efaa7SDag-Erling Smørgrav /* Our verbosity */ 64f7167e0eSDag-Erling Smørgrav static LogLevel log_level = SYSLOG_LEVEL_ERROR; 65761efaa7SDag-Erling Smørgrav 66761efaa7SDag-Erling Smørgrav /* Our client */ 67f7167e0eSDag-Erling Smørgrav static struct passwd *pw = NULL; 68f7167e0eSDag-Erling Smørgrav static char *client_addr = NULL; 6983d2307dSDag-Erling Smørgrav 70b66f2d16SKris Kennaway /* input and output queue */ 71bc5531deSDag-Erling Smørgrav struct sshbuf *iqueue; 72bc5531deSDag-Erling Smørgrav struct sshbuf *oqueue; 73b66f2d16SKris Kennaway 741e8db6e2SBrian Feldman /* Version of client */ 75f7167e0eSDag-Erling Smørgrav static u_int version; 76f7167e0eSDag-Erling Smørgrav 77f7167e0eSDag-Erling Smørgrav /* SSH2_FXP_INIT received */ 78f7167e0eSDag-Erling Smørgrav static int init_done; 791e8db6e2SBrian Feldman 80b15c8340SDag-Erling Smørgrav /* Disable writes */ 81f7167e0eSDag-Erling Smørgrav static int readonly; 82f7167e0eSDag-Erling Smørgrav 83f7167e0eSDag-Erling Smørgrav /* Requests that are allowed/denied */ 8419261079SEd Maste static char *request_allowlist, *request_denylist; 85b15c8340SDag-Erling Smørgrav 86d95e11bfSDag-Erling Smørgrav /* portable attributes, etc. */ 87b66f2d16SKris Kennaway typedef struct Stat Stat; 88b66f2d16SKris Kennaway 891e8db6e2SBrian Feldman struct Stat { 90b66f2d16SKris Kennaway char *name; 91b66f2d16SKris Kennaway char *long_name; 92b66f2d16SKris Kennaway Attrib attrib; 93b66f2d16SKris Kennaway }; 94b66f2d16SKris Kennaway 95f7167e0eSDag-Erling Smørgrav /* Packet handlers */ 96f7167e0eSDag-Erling Smørgrav static void process_open(u_int32_t id); 97f7167e0eSDag-Erling Smørgrav static void process_close(u_int32_t id); 98f7167e0eSDag-Erling Smørgrav static void process_read(u_int32_t id); 99f7167e0eSDag-Erling Smørgrav static void process_write(u_int32_t id); 100f7167e0eSDag-Erling Smørgrav static void process_stat(u_int32_t id); 101f7167e0eSDag-Erling Smørgrav static void process_lstat(u_int32_t id); 102f7167e0eSDag-Erling Smørgrav static void process_fstat(u_int32_t id); 103f7167e0eSDag-Erling Smørgrav static void process_setstat(u_int32_t id); 104f7167e0eSDag-Erling Smørgrav static void process_fsetstat(u_int32_t id); 105f7167e0eSDag-Erling Smørgrav static void process_opendir(u_int32_t id); 106f7167e0eSDag-Erling Smørgrav static void process_readdir(u_int32_t id); 107f7167e0eSDag-Erling Smørgrav static void process_remove(u_int32_t id); 108f7167e0eSDag-Erling Smørgrav static void process_mkdir(u_int32_t id); 109f7167e0eSDag-Erling Smørgrav static void process_rmdir(u_int32_t id); 110f7167e0eSDag-Erling Smørgrav static void process_realpath(u_int32_t id); 111f7167e0eSDag-Erling Smørgrav static void process_rename(u_int32_t id); 112f7167e0eSDag-Erling Smørgrav static void process_readlink(u_int32_t id); 113f7167e0eSDag-Erling Smørgrav static void process_symlink(u_int32_t id); 114f7167e0eSDag-Erling Smørgrav static void process_extended_posix_rename(u_int32_t id); 115f7167e0eSDag-Erling Smørgrav static void process_extended_statvfs(u_int32_t id); 116f7167e0eSDag-Erling Smørgrav static void process_extended_fstatvfs(u_int32_t id); 117f7167e0eSDag-Erling Smørgrav static void process_extended_hardlink(u_int32_t id); 118f7167e0eSDag-Erling Smørgrav static void process_extended_fsync(u_int32_t id); 11919261079SEd Maste static void process_extended_lsetstat(u_int32_t id); 12019261079SEd Maste static void process_extended_limits(u_int32_t id); 12119261079SEd Maste static void process_extended_expand(u_int32_t id); 122f7167e0eSDag-Erling Smørgrav static void process_extended(u_int32_t id); 123f7167e0eSDag-Erling Smørgrav 124f7167e0eSDag-Erling Smørgrav struct sftp_handler { 125f7167e0eSDag-Erling Smørgrav const char *name; /* user-visible name for fine-grained perms */ 126f7167e0eSDag-Erling Smørgrav const char *ext_name; /* extended request name */ 127f7167e0eSDag-Erling Smørgrav u_int type; /* packet type, for non extended packets */ 128f7167e0eSDag-Erling Smørgrav void (*handler)(u_int32_t); 129f7167e0eSDag-Erling Smørgrav int does_write; /* if nonzero, banned for readonly mode */ 130f7167e0eSDag-Erling Smørgrav }; 131f7167e0eSDag-Erling Smørgrav 13219261079SEd Maste static const struct sftp_handler handlers[] = { 133f7167e0eSDag-Erling Smørgrav /* NB. SSH2_FXP_OPEN does the readonly check in the handler itself */ 134f7167e0eSDag-Erling Smørgrav { "open", NULL, SSH2_FXP_OPEN, process_open, 0 }, 135f7167e0eSDag-Erling Smørgrav { "close", NULL, SSH2_FXP_CLOSE, process_close, 0 }, 136f7167e0eSDag-Erling Smørgrav { "read", NULL, SSH2_FXP_READ, process_read, 0 }, 137f7167e0eSDag-Erling Smørgrav { "write", NULL, SSH2_FXP_WRITE, process_write, 1 }, 138f7167e0eSDag-Erling Smørgrav { "lstat", NULL, SSH2_FXP_LSTAT, process_lstat, 0 }, 139f7167e0eSDag-Erling Smørgrav { "fstat", NULL, SSH2_FXP_FSTAT, process_fstat, 0 }, 140f7167e0eSDag-Erling Smørgrav { "setstat", NULL, SSH2_FXP_SETSTAT, process_setstat, 1 }, 141f7167e0eSDag-Erling Smørgrav { "fsetstat", NULL, SSH2_FXP_FSETSTAT, process_fsetstat, 1 }, 142f7167e0eSDag-Erling Smørgrav { "opendir", NULL, SSH2_FXP_OPENDIR, process_opendir, 0 }, 143f7167e0eSDag-Erling Smørgrav { "readdir", NULL, SSH2_FXP_READDIR, process_readdir, 0 }, 144f7167e0eSDag-Erling Smørgrav { "remove", NULL, SSH2_FXP_REMOVE, process_remove, 1 }, 145f7167e0eSDag-Erling Smørgrav { "mkdir", NULL, SSH2_FXP_MKDIR, process_mkdir, 1 }, 146f7167e0eSDag-Erling Smørgrav { "rmdir", NULL, SSH2_FXP_RMDIR, process_rmdir, 1 }, 147f7167e0eSDag-Erling Smørgrav { "realpath", NULL, SSH2_FXP_REALPATH, process_realpath, 0 }, 148f7167e0eSDag-Erling Smørgrav { "stat", NULL, SSH2_FXP_STAT, process_stat, 0 }, 149f7167e0eSDag-Erling Smørgrav { "rename", NULL, SSH2_FXP_RENAME, process_rename, 1 }, 150f7167e0eSDag-Erling Smørgrav { "readlink", NULL, SSH2_FXP_READLINK, process_readlink, 0 }, 151f7167e0eSDag-Erling Smørgrav { "symlink", NULL, SSH2_FXP_SYMLINK, process_symlink, 1 }, 152f7167e0eSDag-Erling Smørgrav { NULL, NULL, 0, NULL, 0 } 153f7167e0eSDag-Erling Smørgrav }; 154f7167e0eSDag-Erling Smørgrav 155f7167e0eSDag-Erling Smørgrav /* SSH2_FXP_EXTENDED submessages */ 15619261079SEd Maste static const struct sftp_handler extended_handlers[] = { 157f7167e0eSDag-Erling Smørgrav { "posix-rename", "posix-rename@openssh.com", 0, 158f7167e0eSDag-Erling Smørgrav process_extended_posix_rename, 1 }, 159f7167e0eSDag-Erling Smørgrav { "statvfs", "statvfs@openssh.com", 0, process_extended_statvfs, 0 }, 160f7167e0eSDag-Erling Smørgrav { "fstatvfs", "fstatvfs@openssh.com", 0, process_extended_fstatvfs, 0 }, 161f7167e0eSDag-Erling Smørgrav { "hardlink", "hardlink@openssh.com", 0, process_extended_hardlink, 1 }, 162f7167e0eSDag-Erling Smørgrav { "fsync", "fsync@openssh.com", 0, process_extended_fsync, 1 }, 16319261079SEd Maste { "lsetstat", "lsetstat@openssh.com", 0, process_extended_lsetstat, 1 }, 16419261079SEd Maste { "limits", "limits@openssh.com", 0, process_extended_limits, 0 }, 16519261079SEd Maste { "expand-path", "expand-path@openssh.com", 0, 16619261079SEd Maste process_extended_expand, 0 }, 167f7167e0eSDag-Erling Smørgrav { NULL, NULL, 0, NULL, 0 } 168f7167e0eSDag-Erling Smørgrav }; 169f7167e0eSDag-Erling Smørgrav 17019261079SEd Maste static const struct sftp_handler * 17119261079SEd Maste extended_handler_byname(const char *name) 17219261079SEd Maste { 17319261079SEd Maste int i; 17419261079SEd Maste 17519261079SEd Maste for (i = 0; extended_handlers[i].handler != NULL; i++) { 17619261079SEd Maste if (strcmp(name, extended_handlers[i].ext_name) == 0) 17719261079SEd Maste return &extended_handlers[i]; 17819261079SEd Maste } 17919261079SEd Maste return NULL; 18019261079SEd Maste } 18119261079SEd Maste 182f7167e0eSDag-Erling Smørgrav static int 18319261079SEd Maste request_permitted(const struct sftp_handler *h) 184f7167e0eSDag-Erling Smørgrav { 185f7167e0eSDag-Erling Smørgrav char *result; 186f7167e0eSDag-Erling Smørgrav 187f7167e0eSDag-Erling Smørgrav if (readonly && h->does_write) { 188f7167e0eSDag-Erling Smørgrav verbose("Refusing %s request in read-only mode", h->name); 189f7167e0eSDag-Erling Smørgrav return 0; 190f7167e0eSDag-Erling Smørgrav } 19119261079SEd Maste if (request_denylist != NULL && 19219261079SEd Maste ((result = match_list(h->name, request_denylist, NULL))) != NULL) { 193f7167e0eSDag-Erling Smørgrav free(result); 19419261079SEd Maste verbose("Refusing denylisted %s request", h->name); 195f7167e0eSDag-Erling Smørgrav return 0; 196f7167e0eSDag-Erling Smørgrav } 19719261079SEd Maste if (request_allowlist != NULL && 19819261079SEd Maste ((result = match_list(h->name, request_allowlist, NULL))) != NULL) { 199f7167e0eSDag-Erling Smørgrav free(result); 20019261079SEd Maste debug2("Permitting allowlisted %s request", h->name); 201f7167e0eSDag-Erling Smørgrav return 1; 202f7167e0eSDag-Erling Smørgrav } 20319261079SEd Maste if (request_allowlist != NULL) { 20419261079SEd Maste verbose("Refusing non-allowlisted %s request", h->name); 205f7167e0eSDag-Erling Smørgrav return 0; 206f7167e0eSDag-Erling Smørgrav } 207f7167e0eSDag-Erling Smørgrav return 1; 208f7167e0eSDag-Erling Smørgrav } 209f7167e0eSDag-Erling Smørgrav 210ae1f160dSDag-Erling Smørgrav static int 211b66f2d16SKris Kennaway errno_to_portable(int unixerrno) 212b66f2d16SKris Kennaway { 213b66f2d16SKris Kennaway int ret = 0; 2141e8db6e2SBrian Feldman 215b66f2d16SKris Kennaway switch (unixerrno) { 216b66f2d16SKris Kennaway case 0: 2171e8db6e2SBrian Feldman ret = SSH2_FX_OK; 218b66f2d16SKris Kennaway break; 219b66f2d16SKris Kennaway case ENOENT: 220b66f2d16SKris Kennaway case ENOTDIR: 221b66f2d16SKris Kennaway case EBADF: 222b66f2d16SKris Kennaway case ELOOP: 2231e8db6e2SBrian Feldman ret = SSH2_FX_NO_SUCH_FILE; 224b66f2d16SKris Kennaway break; 225b66f2d16SKris Kennaway case EPERM: 226b66f2d16SKris Kennaway case EACCES: 227b66f2d16SKris Kennaway case EFAULT: 2281e8db6e2SBrian Feldman ret = SSH2_FX_PERMISSION_DENIED; 229b66f2d16SKris Kennaway break; 230b66f2d16SKris Kennaway case ENAMETOOLONG: 231b66f2d16SKris Kennaway case EINVAL: 2321e8db6e2SBrian Feldman ret = SSH2_FX_BAD_MESSAGE; 233b66f2d16SKris Kennaway break; 234d4af9e69SDag-Erling Smørgrav case ENOSYS: 235d4af9e69SDag-Erling Smørgrav ret = SSH2_FX_OP_UNSUPPORTED; 236d4af9e69SDag-Erling Smørgrav break; 237b66f2d16SKris Kennaway default: 2381e8db6e2SBrian Feldman ret = SSH2_FX_FAILURE; 239b66f2d16SKris Kennaway break; 240b66f2d16SKris Kennaway } 241b66f2d16SKris Kennaway return ret; 242b66f2d16SKris Kennaway } 243b66f2d16SKris Kennaway 244ae1f160dSDag-Erling Smørgrav static int 245b66f2d16SKris Kennaway flags_from_portable(int pflags) 246b66f2d16SKris Kennaway { 247b66f2d16SKris Kennaway int flags = 0; 2481e8db6e2SBrian Feldman 2491e8db6e2SBrian Feldman if ((pflags & SSH2_FXF_READ) && 2501e8db6e2SBrian Feldman (pflags & SSH2_FXF_WRITE)) { 251b66f2d16SKris Kennaway flags = O_RDWR; 2521e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_READ) { 253b66f2d16SKris Kennaway flags = O_RDONLY; 2541e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_WRITE) { 255b66f2d16SKris Kennaway flags = O_WRONLY; 256b66f2d16SKris Kennaway } 257f7167e0eSDag-Erling Smørgrav if (pflags & SSH2_FXF_APPEND) 258f7167e0eSDag-Erling Smørgrav flags |= O_APPEND; 2591e8db6e2SBrian Feldman if (pflags & SSH2_FXF_CREAT) 260b66f2d16SKris Kennaway flags |= O_CREAT; 2611e8db6e2SBrian Feldman if (pflags & SSH2_FXF_TRUNC) 262b66f2d16SKris Kennaway flags |= O_TRUNC; 2631e8db6e2SBrian Feldman if (pflags & SSH2_FXF_EXCL) 264b66f2d16SKris Kennaway flags |= O_EXCL; 265b66f2d16SKris Kennaway return flags; 266b66f2d16SKris Kennaway } 267b66f2d16SKris Kennaway 268761efaa7SDag-Erling Smørgrav static const char * 269761efaa7SDag-Erling Smørgrav string_from_portable(int pflags) 270761efaa7SDag-Erling Smørgrav { 271761efaa7SDag-Erling Smørgrav static char ret[128]; 272761efaa7SDag-Erling Smørgrav 273761efaa7SDag-Erling Smørgrav *ret = '\0'; 274761efaa7SDag-Erling Smørgrav 275761efaa7SDag-Erling Smørgrav #define PAPPEND(str) { \ 276761efaa7SDag-Erling Smørgrav if (*ret != '\0') \ 277761efaa7SDag-Erling Smørgrav strlcat(ret, ",", sizeof(ret)); \ 278761efaa7SDag-Erling Smørgrav strlcat(ret, str, sizeof(ret)); \ 279761efaa7SDag-Erling Smørgrav } 280761efaa7SDag-Erling Smørgrav 281761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_READ) 282761efaa7SDag-Erling Smørgrav PAPPEND("READ") 283761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_WRITE) 284761efaa7SDag-Erling Smørgrav PAPPEND("WRITE") 285f7167e0eSDag-Erling Smørgrav if (pflags & SSH2_FXF_APPEND) 286f7167e0eSDag-Erling Smørgrav PAPPEND("APPEND") 287761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_CREAT) 288761efaa7SDag-Erling Smørgrav PAPPEND("CREATE") 289761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_TRUNC) 290761efaa7SDag-Erling Smørgrav PAPPEND("TRUNCATE") 291761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_EXCL) 292761efaa7SDag-Erling Smørgrav PAPPEND("EXCL") 293761efaa7SDag-Erling Smørgrav 294761efaa7SDag-Erling Smørgrav return ret; 295761efaa7SDag-Erling Smørgrav } 296761efaa7SDag-Erling Smørgrav 297b66f2d16SKris Kennaway /* handle handles */ 298b66f2d16SKris Kennaway 299b66f2d16SKris Kennaway typedef struct Handle Handle; 300b66f2d16SKris Kennaway struct Handle { 301b66f2d16SKris Kennaway int use; 302b66f2d16SKris Kennaway DIR *dirp; 303b66f2d16SKris Kennaway int fd; 304f7167e0eSDag-Erling Smørgrav int flags; 305b66f2d16SKris Kennaway char *name; 306761efaa7SDag-Erling Smørgrav u_int64_t bytes_read, bytes_write; 307d4af9e69SDag-Erling Smørgrav int next_unused; 308b66f2d16SKris Kennaway }; 3091e8db6e2SBrian Feldman 310b66f2d16SKris Kennaway enum { 311b66f2d16SKris Kennaway HANDLE_UNUSED, 312b66f2d16SKris Kennaway HANDLE_DIR, 313b66f2d16SKris Kennaway HANDLE_FILE 314b66f2d16SKris Kennaway }; 3151e8db6e2SBrian Feldman 31619261079SEd Maste static Handle *handles = NULL; 31719261079SEd Maste static u_int num_handles = 0; 31819261079SEd Maste static int first_unused_handle = -1; 319b66f2d16SKris Kennaway 320d4af9e69SDag-Erling Smørgrav static void handle_unused(int i) 321b66f2d16SKris Kennaway { 322b66f2d16SKris Kennaway handles[i].use = HANDLE_UNUSED; 323d4af9e69SDag-Erling Smørgrav handles[i].next_unused = first_unused_handle; 324d4af9e69SDag-Erling Smørgrav first_unused_handle = i; 325b66f2d16SKris Kennaway } 326b66f2d16SKris Kennaway 327ae1f160dSDag-Erling Smørgrav static int 328f7167e0eSDag-Erling Smørgrav handle_new(int use, const char *name, int fd, int flags, DIR *dirp) 329b66f2d16SKris Kennaway { 330d4af9e69SDag-Erling Smørgrav int i; 3311e8db6e2SBrian Feldman 332d4af9e69SDag-Erling Smørgrav if (first_unused_handle == -1) { 333d4af9e69SDag-Erling Smørgrav if (num_handles + 1 <= num_handles) 334d4af9e69SDag-Erling Smørgrav return -1; 335d4af9e69SDag-Erling Smørgrav num_handles++; 336557f75e5SDag-Erling Smørgrav handles = xreallocarray(handles, num_handles, sizeof(Handle)); 337d4af9e69SDag-Erling Smørgrav handle_unused(num_handles - 1); 338d4af9e69SDag-Erling Smørgrav } 339d4af9e69SDag-Erling Smørgrav 340d4af9e69SDag-Erling Smørgrav i = first_unused_handle; 341d4af9e69SDag-Erling Smørgrav first_unused_handle = handles[i].next_unused; 342d4af9e69SDag-Erling Smørgrav 343b66f2d16SKris Kennaway handles[i].use = use; 344b66f2d16SKris Kennaway handles[i].dirp = dirp; 345b66f2d16SKris Kennaway handles[i].fd = fd; 346f7167e0eSDag-Erling Smørgrav handles[i].flags = flags; 347d0c8c0bcSDag-Erling Smørgrav handles[i].name = xstrdup(name); 348761efaa7SDag-Erling Smørgrav handles[i].bytes_read = handles[i].bytes_write = 0; 349d4af9e69SDag-Erling Smørgrav 350b66f2d16SKris Kennaway return i; 351b66f2d16SKris Kennaway } 352b66f2d16SKris Kennaway 353ae1f160dSDag-Erling Smørgrav static int 354b66f2d16SKris Kennaway handle_is_ok(int i, int type) 355b66f2d16SKris Kennaway { 356d4af9e69SDag-Erling Smørgrav return i >= 0 && (u_int)i < num_handles && handles[i].use == type; 357b66f2d16SKris Kennaway } 358b66f2d16SKris Kennaway 359ae1f160dSDag-Erling Smørgrav static int 360bc5531deSDag-Erling Smørgrav handle_to_string(int handle, u_char **stringp, int *hlenp) 361b66f2d16SKris Kennaway { 362b66f2d16SKris Kennaway if (stringp == NULL || hlenp == NULL) 363b66f2d16SKris Kennaway return -1; 3641e8db6e2SBrian Feldman *stringp = xmalloc(sizeof(int32_t)); 365761efaa7SDag-Erling Smørgrav put_u32(*stringp, handle); 3661e8db6e2SBrian Feldman *hlenp = sizeof(int32_t); 367b66f2d16SKris Kennaway return 0; 368b66f2d16SKris Kennaway } 369b66f2d16SKris Kennaway 370ae1f160dSDag-Erling Smørgrav static int 371bc5531deSDag-Erling Smørgrav handle_from_string(const u_char *handle, u_int hlen) 372b66f2d16SKris Kennaway { 3731e8db6e2SBrian Feldman int val; 3741e8db6e2SBrian Feldman 3751e8db6e2SBrian Feldman if (hlen != sizeof(int32_t)) 376b66f2d16SKris Kennaway return -1; 377761efaa7SDag-Erling Smørgrav val = get_u32(handle); 378b66f2d16SKris Kennaway if (handle_is_ok(val, HANDLE_FILE) || 379b66f2d16SKris Kennaway handle_is_ok(val, HANDLE_DIR)) 380b66f2d16SKris Kennaway return val; 381b66f2d16SKris Kennaway return -1; 382b66f2d16SKris Kennaway } 383b66f2d16SKris Kennaway 384ae1f160dSDag-Erling Smørgrav static char * 385b66f2d16SKris Kennaway handle_to_name(int handle) 386b66f2d16SKris Kennaway { 387b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)|| 388b66f2d16SKris Kennaway handle_is_ok(handle, HANDLE_FILE)) 389b66f2d16SKris Kennaway return handles[handle].name; 390b66f2d16SKris Kennaway return NULL; 391b66f2d16SKris Kennaway } 392b66f2d16SKris Kennaway 393ae1f160dSDag-Erling Smørgrav static DIR * 394b66f2d16SKris Kennaway handle_to_dir(int handle) 395b66f2d16SKris Kennaway { 396b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)) 397b66f2d16SKris Kennaway return handles[handle].dirp; 398b66f2d16SKris Kennaway return NULL; 399b66f2d16SKris Kennaway } 400b66f2d16SKris Kennaway 401ae1f160dSDag-Erling Smørgrav static int 402b66f2d16SKris Kennaway handle_to_fd(int handle) 403b66f2d16SKris Kennaway { 404b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) 405b66f2d16SKris Kennaway return handles[handle].fd; 406b66f2d16SKris Kennaway return -1; 407b66f2d16SKris Kennaway } 408b66f2d16SKris Kennaway 409f7167e0eSDag-Erling Smørgrav static int 410f7167e0eSDag-Erling Smørgrav handle_to_flags(int handle) 411f7167e0eSDag-Erling Smørgrav { 412f7167e0eSDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 413f7167e0eSDag-Erling Smørgrav return handles[handle].flags; 414f7167e0eSDag-Erling Smørgrav return 0; 415f7167e0eSDag-Erling Smørgrav } 416f7167e0eSDag-Erling Smørgrav 417761efaa7SDag-Erling Smørgrav static void 418761efaa7SDag-Erling Smørgrav handle_update_read(int handle, ssize_t bytes) 419761efaa7SDag-Erling Smørgrav { 420761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 421761efaa7SDag-Erling Smørgrav handles[handle].bytes_read += bytes; 422761efaa7SDag-Erling Smørgrav } 423761efaa7SDag-Erling Smørgrav 424761efaa7SDag-Erling Smørgrav static void 425761efaa7SDag-Erling Smørgrav handle_update_write(int handle, ssize_t bytes) 426761efaa7SDag-Erling Smørgrav { 427761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 428761efaa7SDag-Erling Smørgrav handles[handle].bytes_write += bytes; 429761efaa7SDag-Erling Smørgrav } 430761efaa7SDag-Erling Smørgrav 431761efaa7SDag-Erling Smørgrav static u_int64_t 432761efaa7SDag-Erling Smørgrav handle_bytes_read(int handle) 433761efaa7SDag-Erling Smørgrav { 434761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 435761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_read); 436761efaa7SDag-Erling Smørgrav return 0; 437761efaa7SDag-Erling Smørgrav } 438761efaa7SDag-Erling Smørgrav 439761efaa7SDag-Erling Smørgrav static u_int64_t 440761efaa7SDag-Erling Smørgrav handle_bytes_write(int handle) 441761efaa7SDag-Erling Smørgrav { 442761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 443761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_write); 444761efaa7SDag-Erling Smørgrav return 0; 445761efaa7SDag-Erling Smørgrav } 446761efaa7SDag-Erling Smørgrav 447ae1f160dSDag-Erling Smørgrav static int 448b66f2d16SKris Kennaway handle_close(int handle) 449b66f2d16SKris Kennaway { 450b66f2d16SKris Kennaway int ret = -1; 4511e8db6e2SBrian Feldman 452b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) { 453b66f2d16SKris Kennaway ret = close(handles[handle].fd); 454e4a9863fSDag-Erling Smørgrav free(handles[handle].name); 455d4af9e69SDag-Erling Smørgrav handle_unused(handle); 456b66f2d16SKris Kennaway } else if (handle_is_ok(handle, HANDLE_DIR)) { 457b66f2d16SKris Kennaway ret = closedir(handles[handle].dirp); 458e4a9863fSDag-Erling Smørgrav free(handles[handle].name); 459d4af9e69SDag-Erling Smørgrav handle_unused(handle); 460b66f2d16SKris Kennaway } else { 461b66f2d16SKris Kennaway errno = ENOENT; 462b66f2d16SKris Kennaway } 463b66f2d16SKris Kennaway return ret; 464b66f2d16SKris Kennaway } 465b66f2d16SKris Kennaway 466761efaa7SDag-Erling Smørgrav static void 467761efaa7SDag-Erling Smørgrav handle_log_close(int handle, char *emsg) 468761efaa7SDag-Erling Smørgrav { 469761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) { 470761efaa7SDag-Erling Smørgrav logit("%s%sclose \"%s\" bytes read %llu written %llu", 471761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 472761efaa7SDag-Erling Smørgrav handle_to_name(handle), 473d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_read(handle), 474d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_write(handle)); 475761efaa7SDag-Erling Smørgrav } else { 476761efaa7SDag-Erling Smørgrav logit("%s%sclosedir \"%s\"", 477761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 478761efaa7SDag-Erling Smørgrav handle_to_name(handle)); 479761efaa7SDag-Erling Smørgrav } 480761efaa7SDag-Erling Smørgrav } 481761efaa7SDag-Erling Smørgrav 482761efaa7SDag-Erling Smørgrav static void 483761efaa7SDag-Erling Smørgrav handle_log_exit(void) 484761efaa7SDag-Erling Smørgrav { 485761efaa7SDag-Erling Smørgrav u_int i; 486761efaa7SDag-Erling Smørgrav 487d4af9e69SDag-Erling Smørgrav for (i = 0; i < num_handles; i++) 488761efaa7SDag-Erling Smørgrav if (handles[i].use != HANDLE_UNUSED) 489761efaa7SDag-Erling Smørgrav handle_log_close(i, "forced"); 490761efaa7SDag-Erling Smørgrav } 491761efaa7SDag-Erling Smørgrav 492ae1f160dSDag-Erling Smørgrav static int 493bc5531deSDag-Erling Smørgrav get_handle(struct sshbuf *queue, int *hp) 494b66f2d16SKris Kennaway { 495bc5531deSDag-Erling Smørgrav u_char *handle; 496bc5531deSDag-Erling Smørgrav int r; 497bc5531deSDag-Erling Smørgrav size_t hlen; 4981e8db6e2SBrian Feldman 499bc5531deSDag-Erling Smørgrav *hp = -1; 500bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_string(queue, &handle, &hlen)) != 0) 501bc5531deSDag-Erling Smørgrav return r; 5021e8db6e2SBrian Feldman if (hlen < 256) 503bc5531deSDag-Erling Smørgrav *hp = handle_from_string(handle, hlen); 504e4a9863fSDag-Erling Smørgrav free(handle); 505bc5531deSDag-Erling Smørgrav return 0; 506b66f2d16SKris Kennaway } 507b66f2d16SKris Kennaway 508b66f2d16SKris Kennaway /* send replies */ 509b66f2d16SKris Kennaway 510ae1f160dSDag-Erling Smørgrav static void 511bc5531deSDag-Erling Smørgrav send_msg(struct sshbuf *m) 512b66f2d16SKris Kennaway { 513bc5531deSDag-Erling Smørgrav int r; 5141e8db6e2SBrian Feldman 515bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_stringb(oqueue, m)) != 0) 51619261079SEd Maste fatal_fr(r, "enqueue"); 517bc5531deSDag-Erling Smørgrav sshbuf_reset(m); 518b66f2d16SKris Kennaway } 519b66f2d16SKris Kennaway 520761efaa7SDag-Erling Smørgrav static const char * 521761efaa7SDag-Erling Smørgrav status_to_message(u_int32_t status) 522b66f2d16SKris Kennaway { 523*1323ec57SEd Maste static const char * const status_messages[] = { 5241e8db6e2SBrian Feldman "Success", /* SSH_FX_OK */ 5251e8db6e2SBrian Feldman "End of file", /* SSH_FX_EOF */ 5261e8db6e2SBrian Feldman "No such file", /* SSH_FX_NO_SUCH_FILE */ 5271e8db6e2SBrian Feldman "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 5281e8db6e2SBrian Feldman "Failure", /* SSH_FX_FAILURE */ 5291e8db6e2SBrian Feldman "Bad message", /* SSH_FX_BAD_MESSAGE */ 5301e8db6e2SBrian Feldman "No connection", /* SSH_FX_NO_CONNECTION */ 5311e8db6e2SBrian Feldman "Connection lost", /* SSH_FX_CONNECTION_LOST */ 5321e8db6e2SBrian Feldman "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 5331e8db6e2SBrian Feldman "Unknown error" /* Others */ 5341e8db6e2SBrian Feldman }; 535ca86bcf2SDag-Erling Smørgrav return (status_messages[MINIMUM(status,SSH2_FX_MAX)]); 536761efaa7SDag-Erling Smørgrav } 5371e8db6e2SBrian Feldman 538761efaa7SDag-Erling Smørgrav static void 539*1323ec57SEd Maste send_status_errmsg(u_int32_t id, u_int32_t status, const char *errmsg) 540761efaa7SDag-Erling Smørgrav { 541bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 542bc5531deSDag-Erling Smørgrav int r; 543761efaa7SDag-Erling Smørgrav 544761efaa7SDag-Erling Smørgrav debug3("request %u: sent status %u", id, status); 545761efaa7SDag-Erling Smørgrav if (log_level > SYSLOG_LEVEL_VERBOSE || 546761efaa7SDag-Erling Smørgrav (status != SSH2_FX_OK && status != SSH2_FX_EOF)) 547761efaa7SDag-Erling Smørgrav logit("sent status %s", status_to_message(status)); 548bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 54919261079SEd Maste fatal_f("sshbuf_new failed"); 550bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_STATUS)) != 0 || 551bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 552bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, status)) != 0) 55319261079SEd Maste fatal_fr(r, "compose"); 5541e8db6e2SBrian Feldman if (version >= 3) { 555*1323ec57SEd Maste if ((r = sshbuf_put_cstring(msg, errmsg == NULL ? 556*1323ec57SEd Maste status_to_message(status) : errmsg)) != 0 || 557bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, "")) != 0) 55819261079SEd Maste fatal_fr(r, "compose message"); 5591e8db6e2SBrian Feldman } 560bc5531deSDag-Erling Smørgrav send_msg(msg); 561bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 562b66f2d16SKris Kennaway } 563*1323ec57SEd Maste 564*1323ec57SEd Maste static void 565*1323ec57SEd Maste send_status(u_int32_t id, u_int32_t status) 566*1323ec57SEd Maste { 567*1323ec57SEd Maste send_status_errmsg(id, status, NULL); 568*1323ec57SEd Maste } 569*1323ec57SEd Maste 570ae1f160dSDag-Erling Smørgrav static void 571bc5531deSDag-Erling Smørgrav send_data_or_handle(char type, u_int32_t id, const u_char *data, int dlen) 572b66f2d16SKris Kennaway { 573bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 574bc5531deSDag-Erling Smørgrav int r; 5751e8db6e2SBrian Feldman 576bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 57719261079SEd Maste fatal_f("sshbuf_new failed"); 578bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, type)) != 0 || 579bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 580bc5531deSDag-Erling Smørgrav (r = sshbuf_put_string(msg, data, dlen)) != 0) 58119261079SEd Maste fatal_fr(r, "compose"); 582bc5531deSDag-Erling Smørgrav send_msg(msg); 583bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 584b66f2d16SKris Kennaway } 585b66f2d16SKris Kennaway 586ae1f160dSDag-Erling Smørgrav static void 587bc5531deSDag-Erling Smørgrav send_data(u_int32_t id, const u_char *data, int dlen) 588b66f2d16SKris Kennaway { 589761efaa7SDag-Erling Smørgrav debug("request %u: sent data len %d", id, dlen); 5901e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 591b66f2d16SKris Kennaway } 592b66f2d16SKris Kennaway 593ae1f160dSDag-Erling Smørgrav static void 594b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle) 595b66f2d16SKris Kennaway { 596bc5531deSDag-Erling Smørgrav u_char *string; 597b66f2d16SKris Kennaway int hlen; 5981e8db6e2SBrian Feldman 599b66f2d16SKris Kennaway handle_to_string(handle, &string, &hlen); 600761efaa7SDag-Erling Smørgrav debug("request %u: sent handle handle %d", id, handle); 6011e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 602e4a9863fSDag-Erling Smørgrav free(string); 603b66f2d16SKris Kennaway } 604b66f2d16SKris Kennaway 605ae1f160dSDag-Erling Smørgrav static void 606efcad6b7SDag-Erling Smørgrav send_names(u_int32_t id, int count, const Stat *stats) 607b66f2d16SKris Kennaway { 608bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 609bc5531deSDag-Erling Smørgrav int i, r; 6101e8db6e2SBrian Feldman 611bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 61219261079SEd Maste fatal_f("sshbuf_new failed"); 613bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_NAME)) != 0 || 614bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 615bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, count)) != 0) 61619261079SEd Maste fatal_fr(r, "compose"); 617761efaa7SDag-Erling Smørgrav debug("request %u: sent names count %d", id, count); 618b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 619bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_cstring(msg, stats[i].name)) != 0 || 620bc5531deSDag-Erling Smørgrav (r = sshbuf_put_cstring(msg, stats[i].long_name)) != 0 || 621bc5531deSDag-Erling Smørgrav (r = encode_attrib(msg, &stats[i].attrib)) != 0) 62219261079SEd Maste fatal_fr(r, "compose filenames/attrib"); 623b66f2d16SKris Kennaway } 624bc5531deSDag-Erling Smørgrav send_msg(msg); 625bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 626b66f2d16SKris Kennaway } 627b66f2d16SKris Kennaway 628ae1f160dSDag-Erling Smørgrav static void 629efcad6b7SDag-Erling Smørgrav send_attrib(u_int32_t id, const Attrib *a) 630b66f2d16SKris Kennaway { 631bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 632bc5531deSDag-Erling Smørgrav int r; 6331e8db6e2SBrian Feldman 634761efaa7SDag-Erling Smørgrav debug("request %u: sent attrib have 0x%x", id, a->flags); 635bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 63619261079SEd Maste fatal_f("sshbuf_new failed"); 637bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_ATTRS)) != 0 || 638bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 639bc5531deSDag-Erling Smørgrav (r = encode_attrib(msg, a)) != 0) 64019261079SEd Maste fatal_fr(r, "compose"); 641bc5531deSDag-Erling Smørgrav send_msg(msg); 642bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 643b66f2d16SKris Kennaway } 644b66f2d16SKris Kennaway 645d4af9e69SDag-Erling Smørgrav static void 646d4af9e69SDag-Erling Smørgrav send_statvfs(u_int32_t id, struct statvfs *st) 647d4af9e69SDag-Erling Smørgrav { 648bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 649d4af9e69SDag-Erling Smørgrav u_int64_t flag; 650bc5531deSDag-Erling Smørgrav int r; 651d4af9e69SDag-Erling Smørgrav 652d4af9e69SDag-Erling Smørgrav flag = (st->f_flag & ST_RDONLY) ? SSH2_FXE_STATVFS_ST_RDONLY : 0; 653d4af9e69SDag-Erling Smørgrav flag |= (st->f_flag & ST_NOSUID) ? SSH2_FXE_STATVFS_ST_NOSUID : 0; 654d4af9e69SDag-Erling Smørgrav 655bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 65619261079SEd Maste fatal_f("sshbuf_new failed"); 657bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED_REPLY)) != 0 || 658bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u32(msg, id)) != 0 || 659bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_bsize)) != 0 || 660bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_frsize)) != 0 || 661bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_blocks)) != 0 || 662bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_bfree)) != 0 || 663bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_bavail)) != 0 || 664bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_files)) != 0 || 665bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_ffree)) != 0 || 666bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_favail)) != 0 || 667bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, FSID_TO_ULONG(st->f_fsid))) != 0 || 668bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, flag)) != 0 || 669bc5531deSDag-Erling Smørgrav (r = sshbuf_put_u64(msg, st->f_namemax)) != 0) 67019261079SEd Maste fatal_fr(r, "compose"); 671bc5531deSDag-Erling Smørgrav send_msg(msg); 672bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 673d4af9e69SDag-Erling Smørgrav } 674d4af9e69SDag-Erling Smørgrav 67519261079SEd Maste /* 67619261079SEd Maste * Prepare SSH2_FXP_VERSION extension advertisement for a single extension. 677*1323ec57SEd Maste * The extension is checked for permission prior to advertisement. 67819261079SEd Maste */ 67919261079SEd Maste static int 68019261079SEd Maste compose_extension(struct sshbuf *msg, const char *name, const char *ver) 68119261079SEd Maste { 68219261079SEd Maste int r; 68319261079SEd Maste const struct sftp_handler *exthnd; 68419261079SEd Maste 68519261079SEd Maste if ((exthnd = extended_handler_byname(name)) == NULL) 68619261079SEd Maste fatal_f("internal error: no handler for %s", name); 68719261079SEd Maste if (!request_permitted(exthnd)) { 68819261079SEd Maste debug2_f("refusing to advertise disallowed extension %s", name); 68919261079SEd Maste return 0; 69019261079SEd Maste } 69119261079SEd Maste if ((r = sshbuf_put_cstring(msg, name)) != 0 || 69219261079SEd Maste (r = sshbuf_put_cstring(msg, ver)) != 0) 69319261079SEd Maste fatal_fr(r, "compose %s", name); 69419261079SEd Maste return 0; 69519261079SEd Maste } 69619261079SEd Maste 697b66f2d16SKris Kennaway /* parse incoming */ 698b66f2d16SKris Kennaway 699ae1f160dSDag-Erling Smørgrav static void 700b66f2d16SKris Kennaway process_init(void) 701b66f2d16SKris Kennaway { 702bc5531deSDag-Erling Smørgrav struct sshbuf *msg; 703bc5531deSDag-Erling Smørgrav int r; 704b66f2d16SKris Kennaway 705bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(iqueue, &version)) != 0) 70619261079SEd Maste fatal_fr(r, "parse"); 707e146993eSDag-Erling Smørgrav verbose("received client version %u", version); 708bc5531deSDag-Erling Smørgrav if ((msg = sshbuf_new()) == NULL) 70919261079SEd Maste fatal_f("sshbuf_new failed"); 710bc5531deSDag-Erling Smørgrav if ((r = sshbuf_put_u8(msg, SSH2_FXP_VERSION)) != 0 || 71119261079SEd Maste (r = sshbuf_put_u32(msg, SSH2_FILEXFER_VERSION)) != 0) 71219261079SEd Maste fatal_fr(r, "compose"); 71319261079SEd Maste 714*1323ec57SEd Maste /* extension advertisements */ 71519261079SEd Maste compose_extension(msg, "posix-rename@openssh.com", "1"); 71619261079SEd Maste compose_extension(msg, "statvfs@openssh.com", "2"); 71719261079SEd Maste compose_extension(msg, "fstatvfs@openssh.com", "2"); 71819261079SEd Maste compose_extension(msg, "hardlink@openssh.com", "1"); 71919261079SEd Maste compose_extension(msg, "fsync@openssh.com", "1"); 72019261079SEd Maste compose_extension(msg, "lsetstat@openssh.com", "1"); 72119261079SEd Maste compose_extension(msg, "limits@openssh.com", "1"); 72219261079SEd Maste compose_extension(msg, "expand-path@openssh.com", "1"); 72319261079SEd Maste 724bc5531deSDag-Erling Smørgrav send_msg(msg); 725bc5531deSDag-Erling Smørgrav sshbuf_free(msg); 726b66f2d16SKris Kennaway } 727b66f2d16SKris Kennaway 728ae1f160dSDag-Erling Smørgrav static void 729f7167e0eSDag-Erling Smørgrav process_open(u_int32_t id) 730b66f2d16SKris Kennaway { 731f7167e0eSDag-Erling Smørgrav u_int32_t pflags; 732bc5531deSDag-Erling Smørgrav Attrib a; 733b66f2d16SKris Kennaway char *name; 734bc5531deSDag-Erling Smørgrav int r, handle, fd, flags, mode, status = SSH2_FX_FAILURE; 735b66f2d16SKris Kennaway 736bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 737bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u32(iqueue, &pflags)) != 0 || /* portable flags */ 738bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 73919261079SEd Maste fatal_fr(r, "parse"); 740bc5531deSDag-Erling Smørgrav 741761efaa7SDag-Erling Smørgrav debug3("request %u: open flags %d", id, pflags); 742b66f2d16SKris Kennaway flags = flags_from_portable(pflags); 743bc5531deSDag-Erling Smørgrav mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a.perm : 0666; 744761efaa7SDag-Erling Smørgrav logit("open \"%s\" flags %s mode 0%o", 745761efaa7SDag-Erling Smørgrav name, string_from_portable(pflags), mode); 746b15c8340SDag-Erling Smørgrav if (readonly && 7474f52dfbbSDag-Erling Smørgrav ((flags & O_ACCMODE) != O_RDONLY || 7484f52dfbbSDag-Erling Smørgrav (flags & (O_CREAT|O_TRUNC)) != 0)) { 749f7167e0eSDag-Erling Smørgrav verbose("Refusing open request in read-only mode"); 750b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 751f7167e0eSDag-Erling Smørgrav } else { 752b66f2d16SKris Kennaway fd = open(name, flags, mode); 75319261079SEd Maste if (fd == -1) { 754b66f2d16SKris Kennaway status = errno_to_portable(errno); 755b66f2d16SKris Kennaway } else { 756f7167e0eSDag-Erling Smørgrav handle = handle_new(HANDLE_FILE, name, fd, flags, NULL); 757b66f2d16SKris Kennaway if (handle < 0) { 758b66f2d16SKris Kennaway close(fd); 759b66f2d16SKris Kennaway } else { 760b66f2d16SKris Kennaway send_handle(id, handle); 7611e8db6e2SBrian Feldman status = SSH2_FX_OK; 762b66f2d16SKris Kennaway } 763b66f2d16SKris Kennaway } 764b15c8340SDag-Erling Smørgrav } 7651e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 766b66f2d16SKris Kennaway send_status(id, status); 767e4a9863fSDag-Erling Smørgrav free(name); 768b66f2d16SKris Kennaway } 769b66f2d16SKris Kennaway 770ae1f160dSDag-Erling Smørgrav static void 771f7167e0eSDag-Erling Smørgrav process_close(u_int32_t id) 772b66f2d16SKris Kennaway { 773bc5531deSDag-Erling Smørgrav int r, handle, ret, status = SSH2_FX_FAILURE; 774b66f2d16SKris Kennaway 775bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 77619261079SEd Maste fatal_fr(r, "parse"); 777bc5531deSDag-Erling Smørgrav 778761efaa7SDag-Erling Smørgrav debug3("request %u: close handle %u", id, handle); 779761efaa7SDag-Erling Smørgrav handle_log_close(handle, NULL); 780b66f2d16SKris Kennaway ret = handle_close(handle); 7811e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 782b66f2d16SKris Kennaway send_status(id, status); 783b66f2d16SKris Kennaway } 784b66f2d16SKris Kennaway 785ae1f160dSDag-Erling Smørgrav static void 786f7167e0eSDag-Erling Smørgrav process_read(u_int32_t id) 787b66f2d16SKris Kennaway { 78819261079SEd Maste static u_char *buf; 78919261079SEd Maste static size_t buflen; 790f7167e0eSDag-Erling Smørgrav u_int32_t len; 791bc5531deSDag-Erling Smørgrav int r, handle, fd, ret, status = SSH2_FX_FAILURE; 792b66f2d16SKris Kennaway u_int64_t off; 793b66f2d16SKris Kennaway 794bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0 || 795bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u64(iqueue, &off)) != 0 || 796bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u32(iqueue, &len)) != 0) 79719261079SEd Maste fatal_fr(r, "parse"); 798b66f2d16SKris Kennaway 79919261079SEd Maste debug("request %u: read \"%s\" (handle %d) off %llu len %u", 800761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len); 80119261079SEd Maste if ((fd = handle_to_fd(handle)) == -1) 80219261079SEd Maste goto out; 80319261079SEd Maste if (len > SFTP_MAX_READ_LENGTH) { 80419261079SEd Maste debug2("read change len %u to %u", len, SFTP_MAX_READ_LENGTH); 80519261079SEd Maste len = SFTP_MAX_READ_LENGTH; 806b66f2d16SKris Kennaway } 80719261079SEd Maste if (len > buflen) { 80819261079SEd Maste debug3_f("allocate %zu => %u", buflen, len); 80919261079SEd Maste if ((buf = realloc(NULL, len)) == NULL) 81019261079SEd Maste fatal_f("realloc failed"); 81119261079SEd Maste buflen = len; 81219261079SEd Maste } 81319261079SEd Maste if (lseek(fd, off, SEEK_SET) == -1) { 814b66f2d16SKris Kennaway status = errno_to_portable(errno); 81519261079SEd Maste error_f("seek \"%.100s\": %s", handle_to_name(handle), 81619261079SEd Maste strerror(errno)); 81719261079SEd Maste goto out; 81819261079SEd Maste } 81919261079SEd Maste if (len == 0) { 82019261079SEd Maste /* weird, but not strictly disallowed */ 82119261079SEd Maste ret = 0; 82219261079SEd Maste } else if ((ret = read(fd, buf, len)) == -1) { 823b66f2d16SKris Kennaway status = errno_to_portable(errno); 82419261079SEd Maste error_f("read \"%.100s\": %s", handle_to_name(handle), 82519261079SEd Maste strerror(errno)); 82619261079SEd Maste goto out; 827b66f2d16SKris Kennaway } else if (ret == 0) { 8281e8db6e2SBrian Feldman status = SSH2_FX_EOF; 82919261079SEd Maste goto out; 83019261079SEd Maste } 831b66f2d16SKris Kennaway send_data(id, buf, ret); 832761efaa7SDag-Erling Smørgrav handle_update_read(handle, ret); 83319261079SEd Maste /* success */ 83419261079SEd Maste status = SSH2_FX_OK; 83519261079SEd Maste out: 8361e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 837b66f2d16SKris Kennaway send_status(id, status); 838b66f2d16SKris Kennaway } 839b66f2d16SKris Kennaway 840ae1f160dSDag-Erling Smørgrav static void 841f7167e0eSDag-Erling Smørgrav process_write(u_int32_t id) 842b66f2d16SKris Kennaway { 843b66f2d16SKris Kennaway u_int64_t off; 844bc5531deSDag-Erling Smørgrav size_t len; 845bc5531deSDag-Erling Smørgrav int r, handle, fd, ret, status; 846bc5531deSDag-Erling Smørgrav u_char *data; 847b66f2d16SKris Kennaway 848bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0 || 849bc5531deSDag-Erling Smørgrav (r = sshbuf_get_u64(iqueue, &off)) != 0 || 850bc5531deSDag-Erling Smørgrav (r = sshbuf_get_string(iqueue, &data, &len)) != 0) 85119261079SEd Maste fatal_fr(r, "parse"); 852b66f2d16SKris Kennaway 853bc5531deSDag-Erling Smørgrav debug("request %u: write \"%s\" (handle %d) off %llu len %zu", 854761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len); 855b66f2d16SKris Kennaway fd = handle_to_fd(handle); 856b15c8340SDag-Erling Smørgrav 857b15c8340SDag-Erling Smørgrav if (fd < 0) 858b15c8340SDag-Erling Smørgrav status = SSH2_FX_FAILURE; 859b15c8340SDag-Erling Smørgrav else { 860f7167e0eSDag-Erling Smørgrav if (!(handle_to_flags(handle) & O_APPEND) && 86119261079SEd Maste lseek(fd, off, SEEK_SET) == -1) { 862b66f2d16SKris Kennaway status = errno_to_portable(errno); 86319261079SEd Maste error_f("seek \"%.100s\": %s", handle_to_name(handle), 86419261079SEd Maste strerror(errno)); 865b66f2d16SKris Kennaway } else { 866b66f2d16SKris Kennaway /* XXX ATOMICIO ? */ 867b66f2d16SKris Kennaway ret = write(fd, data, len); 86819261079SEd Maste if (ret == -1) { 869b66f2d16SKris Kennaway status = errno_to_portable(errno); 87019261079SEd Maste error_f("write \"%.100s\": %s", 87119261079SEd Maste handle_to_name(handle), strerror(errno)); 872043840dfSDag-Erling Smørgrav } else if ((size_t)ret == len) { 8731e8db6e2SBrian Feldman status = SSH2_FX_OK; 874761efaa7SDag-Erling Smørgrav handle_update_write(handle, ret); 875b66f2d16SKris Kennaway } else { 87619261079SEd Maste debug2_f("nothing at all written"); 877b15c8340SDag-Erling Smørgrav status = SSH2_FX_FAILURE; 878b66f2d16SKris Kennaway } 879b66f2d16SKris Kennaway } 880b66f2d16SKris Kennaway } 881b66f2d16SKris Kennaway send_status(id, status); 882e4a9863fSDag-Erling Smørgrav free(data); 883b66f2d16SKris Kennaway } 884b66f2d16SKris Kennaway 885ae1f160dSDag-Erling Smørgrav static void 886f7167e0eSDag-Erling Smørgrav process_do_stat(u_int32_t id, int do_lstat) 887b66f2d16SKris Kennaway { 8881e8db6e2SBrian Feldman Attrib a; 889b66f2d16SKris Kennaway struct stat st; 890b66f2d16SKris Kennaway char *name; 891bc5531deSDag-Erling Smørgrav int r, status = SSH2_FX_FAILURE; 892b66f2d16SKris Kennaway 893bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) 89419261079SEd Maste fatal_fr(r, "parse"); 895bc5531deSDag-Erling Smørgrav 896761efaa7SDag-Erling Smørgrav debug3("request %u: %sstat", id, do_lstat ? "l" : ""); 897761efaa7SDag-Erling Smørgrav verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); 898bc5531deSDag-Erling Smørgrav r = do_lstat ? lstat(name, &st) : stat(name, &st); 89919261079SEd Maste if (r == -1) { 900b66f2d16SKris Kennaway status = errno_to_portable(errno); 901b66f2d16SKris Kennaway } else { 9021e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 9031e8db6e2SBrian Feldman send_attrib(id, &a); 9041e8db6e2SBrian Feldman status = SSH2_FX_OK; 905b66f2d16SKris Kennaway } 9061e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 907b66f2d16SKris Kennaway send_status(id, status); 908e4a9863fSDag-Erling Smørgrav free(name); 909b66f2d16SKris Kennaway } 910b66f2d16SKris Kennaway 911ae1f160dSDag-Erling Smørgrav static void 912f7167e0eSDag-Erling Smørgrav process_stat(u_int32_t id) 913b66f2d16SKris Kennaway { 914f7167e0eSDag-Erling Smørgrav process_do_stat(id, 0); 915b66f2d16SKris Kennaway } 916b66f2d16SKris Kennaway 917ae1f160dSDag-Erling Smørgrav static void 918f7167e0eSDag-Erling Smørgrav process_lstat(u_int32_t id) 919b66f2d16SKris Kennaway { 920f7167e0eSDag-Erling Smørgrav process_do_stat(id, 1); 921b66f2d16SKris Kennaway } 922b66f2d16SKris Kennaway 923ae1f160dSDag-Erling Smørgrav static void 924f7167e0eSDag-Erling Smørgrav process_fstat(u_int32_t id) 925b66f2d16SKris Kennaway { 9261e8db6e2SBrian Feldman Attrib a; 927b66f2d16SKris Kennaway struct stat st; 928bc5531deSDag-Erling Smørgrav int fd, r, handle, status = SSH2_FX_FAILURE; 929b66f2d16SKris Kennaway 930bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 93119261079SEd Maste fatal_fr(r, "parse"); 932761efaa7SDag-Erling Smørgrav debug("request %u: fstat \"%s\" (handle %u)", 933761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle); 934b66f2d16SKris Kennaway fd = handle_to_fd(handle); 935b66f2d16SKris Kennaway if (fd >= 0) { 936bc5531deSDag-Erling Smørgrav r = fstat(fd, &st); 93719261079SEd Maste if (r == -1) { 938b66f2d16SKris Kennaway status = errno_to_portable(errno); 939b66f2d16SKris Kennaway } else { 9401e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 9411e8db6e2SBrian Feldman send_attrib(id, &a); 9421e8db6e2SBrian Feldman status = SSH2_FX_OK; 943b66f2d16SKris Kennaway } 944b66f2d16SKris Kennaway } 9451e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 946b66f2d16SKris Kennaway send_status(id, status); 947b66f2d16SKris Kennaway } 948b66f2d16SKris Kennaway 949ae1f160dSDag-Erling Smørgrav static struct timeval * 950efcad6b7SDag-Erling Smørgrav attrib_to_tv(const Attrib *a) 951b66f2d16SKris Kennaway { 952b66f2d16SKris Kennaway static struct timeval tv[2]; 9531e8db6e2SBrian Feldman 954b66f2d16SKris Kennaway tv[0].tv_sec = a->atime; 955b66f2d16SKris Kennaway tv[0].tv_usec = 0; 956b66f2d16SKris Kennaway tv[1].tv_sec = a->mtime; 957b66f2d16SKris Kennaway tv[1].tv_usec = 0; 958b66f2d16SKris Kennaway return tv; 959b66f2d16SKris Kennaway } 960b66f2d16SKris Kennaway 96119261079SEd Maste static struct timespec * 96219261079SEd Maste attrib_to_ts(const Attrib *a) 96319261079SEd Maste { 96419261079SEd Maste static struct timespec ts[2]; 96519261079SEd Maste 96619261079SEd Maste ts[0].tv_sec = a->atime; 96719261079SEd Maste ts[0].tv_nsec = 0; 96819261079SEd Maste ts[1].tv_sec = a->mtime; 96919261079SEd Maste ts[1].tv_nsec = 0; 97019261079SEd Maste return ts; 97119261079SEd Maste } 97219261079SEd Maste 973ae1f160dSDag-Erling Smørgrav static void 974f7167e0eSDag-Erling Smørgrav process_setstat(u_int32_t id) 975b66f2d16SKris Kennaway { 976bc5531deSDag-Erling Smørgrav Attrib a; 977b66f2d16SKris Kennaway char *name; 978bc5531deSDag-Erling Smørgrav int r, status = SSH2_FX_OK; 979b66f2d16SKris Kennaway 980bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 981bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 98219261079SEd Maste fatal_fr(r, "parse"); 983bc5531deSDag-Erling Smørgrav 984761efaa7SDag-Erling Smørgrav debug("request %u: setstat name \"%s\"", id, name); 985bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { 986d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu", 987bc5531deSDag-Erling Smørgrav name, (unsigned long long)a.size); 988bc5531deSDag-Erling Smørgrav r = truncate(name, a.size); 989bc5531deSDag-Erling Smørgrav if (r == -1) 990ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 991ae1f160dSDag-Erling Smørgrav } 992bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 993bc5531deSDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a.perm); 994bc5531deSDag-Erling Smørgrav r = chmod(name, a.perm & 07777); 995bc5531deSDag-Erling Smørgrav if (r == -1) 996b66f2d16SKris Kennaway status = errno_to_portable(errno); 997b66f2d16SKris Kennaway } 998bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 999761efaa7SDag-Erling Smørgrav char buf[64]; 1000bc5531deSDag-Erling Smørgrav time_t t = a.mtime; 1001761efaa7SDag-Erling Smørgrav 1002761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 1003761efaa7SDag-Erling Smørgrav localtime(&t)); 1004761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf); 1005bc5531deSDag-Erling Smørgrav r = utimes(name, attrib_to_tv(&a)); 1006bc5531deSDag-Erling Smørgrav if (r == -1) 1007b66f2d16SKris Kennaway status = errno_to_portable(errno); 1008b66f2d16SKris Kennaway } 1009bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { 1010761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name, 1011bc5531deSDag-Erling Smørgrav (u_long)a.uid, (u_long)a.gid); 1012bc5531deSDag-Erling Smørgrav r = chown(name, a.uid, a.gid); 1013bc5531deSDag-Erling Smørgrav if (r == -1) 10141e8db6e2SBrian Feldman status = errno_to_portable(errno); 10151e8db6e2SBrian Feldman } 1016b66f2d16SKris Kennaway send_status(id, status); 1017e4a9863fSDag-Erling Smørgrav free(name); 1018b66f2d16SKris Kennaway } 1019b66f2d16SKris Kennaway 1020ae1f160dSDag-Erling Smørgrav static void 1021f7167e0eSDag-Erling Smørgrav process_fsetstat(u_int32_t id) 1022b66f2d16SKris Kennaway { 1023bc5531deSDag-Erling Smørgrav Attrib a; 1024bc5531deSDag-Erling Smørgrav int handle, fd, r; 10251e8db6e2SBrian Feldman int status = SSH2_FX_OK; 1026b66f2d16SKris Kennaway 1027bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0 || 1028bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 102919261079SEd Maste fatal_fr(r, "parse"); 1030bc5531deSDag-Erling Smørgrav 1031761efaa7SDag-Erling Smørgrav debug("request %u: fsetstat handle %d", id, handle); 1032b66f2d16SKris Kennaway fd = handle_to_fd(handle); 1033b15c8340SDag-Erling Smørgrav if (fd < 0) 10341e8db6e2SBrian Feldman status = SSH2_FX_FAILURE; 1035b15c8340SDag-Erling Smørgrav else { 1036761efaa7SDag-Erling Smørgrav char *name = handle_to_name(handle); 1037761efaa7SDag-Erling Smørgrav 1038bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { 1039d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu", 1040bc5531deSDag-Erling Smørgrav name, (unsigned long long)a.size); 1041bc5531deSDag-Erling Smørgrav r = ftruncate(fd, a.size); 1042bc5531deSDag-Erling Smørgrav if (r == -1) 1043ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 1044ae1f160dSDag-Erling Smørgrav } 1045bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 1046bc5531deSDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a.perm); 104783d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 1048bc5531deSDag-Erling Smørgrav r = fchmod(fd, a.perm & 07777); 104983d2307dSDag-Erling Smørgrav #else 1050bc5531deSDag-Erling Smørgrav r = chmod(name, a.perm & 07777); 105183d2307dSDag-Erling Smørgrav #endif 1052bc5531deSDag-Erling Smørgrav if (r == -1) 1053b66f2d16SKris Kennaway status = errno_to_portable(errno); 1054b66f2d16SKris Kennaway } 1055bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 1056761efaa7SDag-Erling Smørgrav char buf[64]; 1057bc5531deSDag-Erling Smørgrav time_t t = a.mtime; 1058761efaa7SDag-Erling Smørgrav 1059761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 1060761efaa7SDag-Erling Smørgrav localtime(&t)); 1061761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf); 106283d2307dSDag-Erling Smørgrav #ifdef HAVE_FUTIMES 1063bc5531deSDag-Erling Smørgrav r = futimes(fd, attrib_to_tv(&a)); 106483d2307dSDag-Erling Smørgrav #else 1065bc5531deSDag-Erling Smørgrav r = utimes(name, attrib_to_tv(&a)); 106683d2307dSDag-Erling Smørgrav #endif 1067bc5531deSDag-Erling Smørgrav if (r == -1) 1068b66f2d16SKris Kennaway status = errno_to_portable(errno); 1069b66f2d16SKris Kennaway } 1070bc5531deSDag-Erling Smørgrav if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { 1071761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name, 1072bc5531deSDag-Erling Smørgrav (u_long)a.uid, (u_long)a.gid); 107383d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHOWN 1074bc5531deSDag-Erling Smørgrav r = fchown(fd, a.uid, a.gid); 107583d2307dSDag-Erling Smørgrav #else 1076bc5531deSDag-Erling Smørgrav r = chown(name, a.uid, a.gid); 107783d2307dSDag-Erling Smørgrav #endif 1078bc5531deSDag-Erling Smørgrav if (r == -1) 10791e8db6e2SBrian Feldman status = errno_to_portable(errno); 10801e8db6e2SBrian Feldman } 1081b66f2d16SKris Kennaway } 1082b66f2d16SKris Kennaway send_status(id, status); 1083b66f2d16SKris Kennaway } 1084b66f2d16SKris Kennaway 1085ae1f160dSDag-Erling Smørgrav static void 1086f7167e0eSDag-Erling Smørgrav process_opendir(u_int32_t id) 1087b66f2d16SKris Kennaway { 1088b66f2d16SKris Kennaway DIR *dirp = NULL; 1089b66f2d16SKris Kennaway char *path; 1090bc5531deSDag-Erling Smørgrav int r, handle, status = SSH2_FX_FAILURE; 1091b66f2d16SKris Kennaway 1092bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 109319261079SEd Maste fatal_fr(r, "parse"); 1094bc5531deSDag-Erling Smørgrav 1095761efaa7SDag-Erling Smørgrav debug3("request %u: opendir", id); 1096761efaa7SDag-Erling Smørgrav logit("opendir \"%s\"", path); 1097b66f2d16SKris Kennaway dirp = opendir(path); 1098b66f2d16SKris Kennaway if (dirp == NULL) { 1099b66f2d16SKris Kennaway status = errno_to_portable(errno); 1100b66f2d16SKris Kennaway } else { 1101f7167e0eSDag-Erling Smørgrav handle = handle_new(HANDLE_DIR, path, 0, 0, dirp); 1102b66f2d16SKris Kennaway if (handle < 0) { 1103b66f2d16SKris Kennaway closedir(dirp); 1104b66f2d16SKris Kennaway } else { 1105b66f2d16SKris Kennaway send_handle(id, handle); 11061e8db6e2SBrian Feldman status = SSH2_FX_OK; 1107b66f2d16SKris Kennaway } 1108b66f2d16SKris Kennaway 1109b66f2d16SKris Kennaway } 11101e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 1111b66f2d16SKris Kennaway send_status(id, status); 1112e4a9863fSDag-Erling Smørgrav free(path); 1113b66f2d16SKris Kennaway } 1114b66f2d16SKris Kennaway 1115ae1f160dSDag-Erling Smørgrav static void 1116f7167e0eSDag-Erling Smørgrav process_readdir(u_int32_t id) 1117b66f2d16SKris Kennaway { 1118b66f2d16SKris Kennaway DIR *dirp; 1119b66f2d16SKris Kennaway struct dirent *dp; 1120b66f2d16SKris Kennaway char *path; 1121bc5531deSDag-Erling Smørgrav int r, handle; 1122b66f2d16SKris Kennaway 1123bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 112419261079SEd Maste fatal_fr(r, "parse"); 1125bc5531deSDag-Erling Smørgrav 1126761efaa7SDag-Erling Smørgrav debug("request %u: readdir \"%s\" (handle %d)", id, 1127761efaa7SDag-Erling Smørgrav handle_to_name(handle), handle); 1128b66f2d16SKris Kennaway dirp = handle_to_dir(handle); 1129b66f2d16SKris Kennaway path = handle_to_name(handle); 1130b66f2d16SKris Kennaway if (dirp == NULL || path == NULL) { 11311e8db6e2SBrian Feldman send_status(id, SSH2_FX_FAILURE); 1132b66f2d16SKris Kennaway } else { 1133b66f2d16SKris Kennaway struct stat st; 1134bc5531deSDag-Erling Smørgrav char pathname[PATH_MAX]; 1135b66f2d16SKris Kennaway Stat *stats; 1136b66f2d16SKris Kennaway int nstats = 10, count = 0, i; 1137ee21a45fSDag-Erling Smørgrav 1138761efaa7SDag-Erling Smørgrav stats = xcalloc(nstats, sizeof(Stat)); 1139b66f2d16SKris Kennaway while ((dp = readdir(dirp)) != NULL) { 1140b66f2d16SKris Kennaway if (count >= nstats) { 1141b66f2d16SKris Kennaway nstats *= 2; 1142557f75e5SDag-Erling Smørgrav stats = xreallocarray(stats, nstats, sizeof(Stat)); 1143b66f2d16SKris Kennaway } 1144b66f2d16SKris Kennaway /* XXX OVERFLOW ? */ 1145ae1f160dSDag-Erling Smørgrav snprintf(pathname, sizeof pathname, "%s%s%s", path, 1146ae1f160dSDag-Erling Smørgrav strcmp(path, "/") ? "/" : "", dp->d_name); 114719261079SEd Maste if (lstat(pathname, &st) == -1) 1148b66f2d16SKris Kennaway continue; 11491e8db6e2SBrian Feldman stat_to_attrib(&st, &(stats[count].attrib)); 1150b66f2d16SKris Kennaway stats[count].name = xstrdup(dp->d_name); 1151b15c8340SDag-Erling Smørgrav stats[count].long_name = ls_file(dp->d_name, &st, 0, 0); 1152b66f2d16SKris Kennaway count++; 1153b66f2d16SKris Kennaway /* send up to 100 entries in one message */ 11541e8db6e2SBrian Feldman /* XXX check packet size instead */ 1155b66f2d16SKris Kennaway if (count == 100) 1156b66f2d16SKris Kennaway break; 1157b66f2d16SKris Kennaway } 11581e8db6e2SBrian Feldman if (count > 0) { 1159b66f2d16SKris Kennaway send_names(id, count, stats); 1160b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 1161e4a9863fSDag-Erling Smørgrav free(stats[i].name); 1162e4a9863fSDag-Erling Smørgrav free(stats[i].long_name); 1163b66f2d16SKris Kennaway } 11641e8db6e2SBrian Feldman } else { 11651e8db6e2SBrian Feldman send_status(id, SSH2_FX_EOF); 11661e8db6e2SBrian Feldman } 1167e4a9863fSDag-Erling Smørgrav free(stats); 1168b66f2d16SKris Kennaway } 1169b66f2d16SKris Kennaway } 1170b66f2d16SKris Kennaway 1171ae1f160dSDag-Erling Smørgrav static void 1172f7167e0eSDag-Erling Smørgrav process_remove(u_int32_t id) 1173b66f2d16SKris Kennaway { 1174b66f2d16SKris Kennaway char *name; 1175bc5531deSDag-Erling Smørgrav int r, status = SSH2_FX_FAILURE; 1176b66f2d16SKris Kennaway 1177bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) 117819261079SEd Maste fatal_fr(r, "parse"); 1179bc5531deSDag-Erling Smørgrav 1180761efaa7SDag-Erling Smørgrav debug3("request %u: remove", id); 1181761efaa7SDag-Erling Smørgrav logit("remove name \"%s\"", name); 1182bc5531deSDag-Erling Smørgrav r = unlink(name); 1183bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1184b66f2d16SKris Kennaway send_status(id, status); 1185e4a9863fSDag-Erling Smørgrav free(name); 1186b66f2d16SKris Kennaway } 1187b66f2d16SKris Kennaway 1188ae1f160dSDag-Erling Smørgrav static void 1189f7167e0eSDag-Erling Smørgrav process_mkdir(u_int32_t id) 1190b66f2d16SKris Kennaway { 1191bc5531deSDag-Erling Smørgrav Attrib a; 1192b66f2d16SKris Kennaway char *name; 1193bc5531deSDag-Erling Smørgrav int r, mode, status = SSH2_FX_FAILURE; 1194b66f2d16SKris Kennaway 1195bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 1196bc5531deSDag-Erling Smørgrav (r = decode_attrib(iqueue, &a)) != 0) 119719261079SEd Maste fatal_fr(r, "parse"); 1198bc5531deSDag-Erling Smørgrav 1199bc5531deSDag-Erling Smørgrav mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 1200bc5531deSDag-Erling Smørgrav a.perm & 07777 : 0777; 1201761efaa7SDag-Erling Smørgrav debug3("request %u: mkdir", id); 1202761efaa7SDag-Erling Smørgrav logit("mkdir name \"%s\" mode 0%o", name, mode); 1203bc5531deSDag-Erling Smørgrav r = mkdir(name, mode); 1204bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1205b66f2d16SKris Kennaway send_status(id, status); 1206e4a9863fSDag-Erling Smørgrav free(name); 1207b66f2d16SKris Kennaway } 1208b66f2d16SKris Kennaway 1209ae1f160dSDag-Erling Smørgrav static void 1210f7167e0eSDag-Erling Smørgrav process_rmdir(u_int32_t id) 1211b66f2d16SKris Kennaway { 1212b66f2d16SKris Kennaway char *name; 1213bc5531deSDag-Erling Smørgrav int r, status; 1214b66f2d16SKris Kennaway 1215bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) 121619261079SEd Maste fatal_fr(r, "parse"); 1217bc5531deSDag-Erling Smørgrav 1218761efaa7SDag-Erling Smørgrav debug3("request %u: rmdir", id); 1219761efaa7SDag-Erling Smørgrav logit("rmdir name \"%s\"", name); 1220bc5531deSDag-Erling Smørgrav r = rmdir(name); 1221bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1222b66f2d16SKris Kennaway send_status(id, status); 1223e4a9863fSDag-Erling Smørgrav free(name); 1224b66f2d16SKris Kennaway } 1225b66f2d16SKris Kennaway 1226ae1f160dSDag-Erling Smørgrav static void 1227f7167e0eSDag-Erling Smørgrav process_realpath(u_int32_t id) 1228b66f2d16SKris Kennaway { 1229bc5531deSDag-Erling Smørgrav char resolvedname[PATH_MAX]; 1230b66f2d16SKris Kennaway char *path; 1231bc5531deSDag-Erling Smørgrav int r; 1232b66f2d16SKris Kennaway 1233bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 123419261079SEd Maste fatal_fr(r, "parse"); 1235bc5531deSDag-Erling Smørgrav 12361e8db6e2SBrian Feldman if (path[0] == '\0') { 1237e4a9863fSDag-Erling Smørgrav free(path); 12381e8db6e2SBrian Feldman path = xstrdup("."); 12391e8db6e2SBrian Feldman } 1240761efaa7SDag-Erling Smørgrav debug3("request %u: realpath", id); 1241761efaa7SDag-Erling Smørgrav verbose("realpath \"%s\"", path); 124219261079SEd Maste if (sftp_realpath(path, resolvedname) == NULL) { 1243b66f2d16SKris Kennaway send_status(id, errno_to_portable(errno)); 1244b66f2d16SKris Kennaway } else { 1245b66f2d16SKris Kennaway Stat s; 1246b66f2d16SKris Kennaway attrib_clear(&s.attrib); 1247b66f2d16SKris Kennaway s.name = s.long_name = resolvedname; 1248b66f2d16SKris Kennaway send_names(id, 1, &s); 1249b66f2d16SKris Kennaway } 1250e4a9863fSDag-Erling Smørgrav free(path); 1251b66f2d16SKris Kennaway } 1252b66f2d16SKris Kennaway 1253ae1f160dSDag-Erling Smørgrav static void 1254f7167e0eSDag-Erling Smørgrav process_rename(u_int32_t id) 1255b66f2d16SKris Kennaway { 1256b66f2d16SKris Kennaway char *oldpath, *newpath; 1257bc5531deSDag-Erling Smørgrav int r, status; 1258d0c8c0bcSDag-Erling Smørgrav struct stat sb; 1259b66f2d16SKris Kennaway 1260bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1261bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 126219261079SEd Maste fatal_fr(r, "parse"); 1263bc5531deSDag-Erling Smørgrav 1264761efaa7SDag-Erling Smørgrav debug3("request %u: rename", id); 1265761efaa7SDag-Erling Smørgrav logit("rename old \"%s\" new \"%s\"", oldpath, newpath); 1266d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_FAILURE; 1267f7167e0eSDag-Erling Smørgrav if (lstat(oldpath, &sb) == -1) 1268d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1269d0c8c0bcSDag-Erling Smørgrav else if (S_ISREG(sb.st_mode)) { 1270d0c8c0bcSDag-Erling Smørgrav /* Race-free rename of regular files */ 1271d74d50a8SDag-Erling Smørgrav if (link(oldpath, newpath) == -1) { 12727aee6ffeSDag-Erling Smørgrav if (errno == EOPNOTSUPP || errno == ENOSYS 1273d4af9e69SDag-Erling Smørgrav #ifdef EXDEV 1274d4af9e69SDag-Erling Smørgrav || errno == EXDEV 1275d4af9e69SDag-Erling Smørgrav #endif 1276d74d50a8SDag-Erling Smørgrav #ifdef LINK_OPNOTSUPP_ERRNO 1277d74d50a8SDag-Erling Smørgrav || errno == LINK_OPNOTSUPP_ERRNO 1278d74d50a8SDag-Erling Smørgrav #endif 1279d74d50a8SDag-Erling Smørgrav ) { 1280d74d50a8SDag-Erling Smørgrav struct stat st; 1281d74d50a8SDag-Erling Smørgrav 1282d74d50a8SDag-Erling Smørgrav /* 1283d74d50a8SDag-Erling Smørgrav * fs doesn't support links, so fall back to 1284d74d50a8SDag-Erling Smørgrav * stat+rename. This is racy. 1285d74d50a8SDag-Erling Smørgrav */ 1286d74d50a8SDag-Erling Smørgrav if (stat(newpath, &st) == -1) { 1287d74d50a8SDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1288d74d50a8SDag-Erling Smørgrav status = 1289d74d50a8SDag-Erling Smørgrav errno_to_portable(errno); 1290d74d50a8SDag-Erling Smørgrav else 1291d74d50a8SDag-Erling Smørgrav status = SSH2_FX_OK; 1292d74d50a8SDag-Erling Smørgrav } 1293d74d50a8SDag-Erling Smørgrav } else { 1294d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1295d74d50a8SDag-Erling Smørgrav } 1296d74d50a8SDag-Erling Smørgrav } else if (unlink(oldpath) == -1) { 1297d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1298d0c8c0bcSDag-Erling Smørgrav /* clean spare link */ 1299d0c8c0bcSDag-Erling Smørgrav unlink(newpath); 1300d0c8c0bcSDag-Erling Smørgrav } else 1301d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 1302d0c8c0bcSDag-Erling Smørgrav } else if (stat(newpath, &sb) == -1) { 1303d0c8c0bcSDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1304d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1305d0c8c0bcSDag-Erling Smørgrav else 1306d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 13071e8db6e2SBrian Feldman } 1308b66f2d16SKris Kennaway send_status(id, status); 1309e4a9863fSDag-Erling Smørgrav free(oldpath); 1310e4a9863fSDag-Erling Smørgrav free(newpath); 1311b66f2d16SKris Kennaway } 1312b66f2d16SKris Kennaway 1313ae1f160dSDag-Erling Smørgrav static void 1314f7167e0eSDag-Erling Smørgrav process_readlink(u_int32_t id) 13151e8db6e2SBrian Feldman { 1316bc5531deSDag-Erling Smørgrav int r, len; 1317bc5531deSDag-Erling Smørgrav char buf[PATH_MAX]; 13181e8db6e2SBrian Feldman char *path; 13191e8db6e2SBrian Feldman 1320bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 132119261079SEd Maste fatal_fr(r, "parse"); 1322bc5531deSDag-Erling Smørgrav 1323761efaa7SDag-Erling Smørgrav debug3("request %u: readlink", id); 1324761efaa7SDag-Erling Smørgrav verbose("readlink \"%s\"", path); 1325d74d50a8SDag-Erling Smørgrav if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) 13261e8db6e2SBrian Feldman send_status(id, errno_to_portable(errno)); 13271e8db6e2SBrian Feldman else { 13281e8db6e2SBrian Feldman Stat s; 13291e8db6e2SBrian Feldman 1330d74d50a8SDag-Erling Smørgrav buf[len] = '\0'; 13311e8db6e2SBrian Feldman attrib_clear(&s.attrib); 1332d74d50a8SDag-Erling Smørgrav s.name = s.long_name = buf; 13331e8db6e2SBrian Feldman send_names(id, 1, &s); 13341e8db6e2SBrian Feldman } 1335e4a9863fSDag-Erling Smørgrav free(path); 13361e8db6e2SBrian Feldman } 13371e8db6e2SBrian Feldman 1338ae1f160dSDag-Erling Smørgrav static void 1339f7167e0eSDag-Erling Smørgrav process_symlink(u_int32_t id) 13401e8db6e2SBrian Feldman { 13411e8db6e2SBrian Feldman char *oldpath, *newpath; 1342bc5531deSDag-Erling Smørgrav int r, status; 13431e8db6e2SBrian Feldman 1344bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1345bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 134619261079SEd Maste fatal_fr(r, "parse"); 1347bc5531deSDag-Erling Smørgrav 1348761efaa7SDag-Erling Smørgrav debug3("request %u: symlink", id); 1349761efaa7SDag-Erling Smørgrav logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); 1350d0c8c0bcSDag-Erling Smørgrav /* this will fail if 'newpath' exists */ 1351bc5531deSDag-Erling Smørgrav r = symlink(oldpath, newpath); 1352bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 13531e8db6e2SBrian Feldman send_status(id, status); 1354e4a9863fSDag-Erling Smørgrav free(oldpath); 1355e4a9863fSDag-Erling Smørgrav free(newpath); 13561e8db6e2SBrian Feldman } 13571e8db6e2SBrian Feldman 1358ae1f160dSDag-Erling Smørgrav static void 1359d4af9e69SDag-Erling Smørgrav process_extended_posix_rename(u_int32_t id) 1360d4af9e69SDag-Erling Smørgrav { 1361d4af9e69SDag-Erling Smørgrav char *oldpath, *newpath; 1362bc5531deSDag-Erling Smørgrav int r, status; 1363d4af9e69SDag-Erling Smørgrav 1364bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1365bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 136619261079SEd Maste fatal_fr(r, "parse"); 1367bc5531deSDag-Erling Smørgrav 1368d4af9e69SDag-Erling Smørgrav debug3("request %u: posix-rename", id); 1369d4af9e69SDag-Erling Smørgrav logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); 1370bc5531deSDag-Erling Smørgrav r = rename(oldpath, newpath); 1371bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1372b15c8340SDag-Erling Smørgrav send_status(id, status); 1373e4a9863fSDag-Erling Smørgrav free(oldpath); 1374e4a9863fSDag-Erling Smørgrav free(newpath); 1375d4af9e69SDag-Erling Smørgrav } 1376d4af9e69SDag-Erling Smørgrav 1377d4af9e69SDag-Erling Smørgrav static void 1378d4af9e69SDag-Erling Smørgrav process_extended_statvfs(u_int32_t id) 1379d4af9e69SDag-Erling Smørgrav { 1380d4af9e69SDag-Erling Smørgrav char *path; 1381d4af9e69SDag-Erling Smørgrav struct statvfs st; 1382bc5531deSDag-Erling Smørgrav int r; 1383d4af9e69SDag-Erling Smørgrav 1384bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 138519261079SEd Maste fatal_fr(r, "parse"); 1386f7167e0eSDag-Erling Smørgrav debug3("request %u: statvfs", id); 1387f7167e0eSDag-Erling Smørgrav logit("statvfs \"%s\"", path); 1388d4af9e69SDag-Erling Smørgrav 1389d4af9e69SDag-Erling Smørgrav if (statvfs(path, &st) != 0) 1390d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1391d4af9e69SDag-Erling Smørgrav else 1392d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st); 1393e4a9863fSDag-Erling Smørgrav free(path); 1394d4af9e69SDag-Erling Smørgrav } 1395d4af9e69SDag-Erling Smørgrav 1396d4af9e69SDag-Erling Smørgrav static void 1397d4af9e69SDag-Erling Smørgrav process_extended_fstatvfs(u_int32_t id) 1398d4af9e69SDag-Erling Smørgrav { 1399bc5531deSDag-Erling Smørgrav int r, handle, fd; 1400d4af9e69SDag-Erling Smørgrav struct statvfs st; 1401d4af9e69SDag-Erling Smørgrav 1402bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 140319261079SEd Maste fatal_fr(r, "parse"); 1404d4af9e69SDag-Erling Smørgrav debug("request %u: fstatvfs \"%s\" (handle %u)", 1405d4af9e69SDag-Erling Smørgrav id, handle_to_name(handle), handle); 1406d4af9e69SDag-Erling Smørgrav if ((fd = handle_to_fd(handle)) < 0) { 1407d4af9e69SDag-Erling Smørgrav send_status(id, SSH2_FX_FAILURE); 1408d4af9e69SDag-Erling Smørgrav return; 1409d4af9e69SDag-Erling Smørgrav } 1410d4af9e69SDag-Erling Smørgrav if (fstatvfs(fd, &st) != 0) 1411d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1412d4af9e69SDag-Erling Smørgrav else 1413d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st); 1414d4af9e69SDag-Erling Smørgrav } 1415d4af9e69SDag-Erling Smørgrav 1416d4af9e69SDag-Erling Smørgrav static void 14174a421b63SDag-Erling Smørgrav process_extended_hardlink(u_int32_t id) 14184a421b63SDag-Erling Smørgrav { 14194a421b63SDag-Erling Smørgrav char *oldpath, *newpath; 1420bc5531deSDag-Erling Smørgrav int r, status; 14214a421b63SDag-Erling Smørgrav 1422bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 || 1423bc5531deSDag-Erling Smørgrav (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0) 142419261079SEd Maste fatal_fr(r, "parse"); 1425bc5531deSDag-Erling Smørgrav 14264a421b63SDag-Erling Smørgrav debug3("request %u: hardlink", id); 14274a421b63SDag-Erling Smørgrav logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath); 1428bc5531deSDag-Erling Smørgrav r = link(oldpath, newpath); 1429bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 14304a421b63SDag-Erling Smørgrav send_status(id, status); 1431e4a9863fSDag-Erling Smørgrav free(oldpath); 1432e4a9863fSDag-Erling Smørgrav free(newpath); 14334a421b63SDag-Erling Smørgrav } 14344a421b63SDag-Erling Smørgrav 14354a421b63SDag-Erling Smørgrav static void 1436f7167e0eSDag-Erling Smørgrav process_extended_fsync(u_int32_t id) 14371e8db6e2SBrian Feldman { 1438bc5531deSDag-Erling Smørgrav int handle, fd, r, status = SSH2_FX_OP_UNSUPPORTED; 14391e8db6e2SBrian Feldman 1440bc5531deSDag-Erling Smørgrav if ((r = get_handle(iqueue, &handle)) != 0) 144119261079SEd Maste fatal_fr(r, "parse"); 1442f7167e0eSDag-Erling Smørgrav debug3("request %u: fsync (handle %u)", id, handle); 1443f7167e0eSDag-Erling Smørgrav verbose("fsync \"%s\"", handle_to_name(handle)); 1444f7167e0eSDag-Erling Smørgrav if ((fd = handle_to_fd(handle)) < 0) 1445f7167e0eSDag-Erling Smørgrav status = SSH2_FX_NO_SUCH_FILE; 1446f7167e0eSDag-Erling Smørgrav else if (handle_is_ok(handle, HANDLE_FILE)) { 1447bc5531deSDag-Erling Smørgrav r = fsync(fd); 1448bc5531deSDag-Erling Smørgrav status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1449f7167e0eSDag-Erling Smørgrav } 1450f7167e0eSDag-Erling Smørgrav send_status(id, status); 1451f7167e0eSDag-Erling Smørgrav } 1452f7167e0eSDag-Erling Smørgrav 1453f7167e0eSDag-Erling Smørgrav static void 145419261079SEd Maste process_extended_lsetstat(u_int32_t id) 145519261079SEd Maste { 145619261079SEd Maste Attrib a; 145719261079SEd Maste char *name; 145819261079SEd Maste int r, status = SSH2_FX_OK; 145919261079SEd Maste 146019261079SEd Maste if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || 146119261079SEd Maste (r = decode_attrib(iqueue, &a)) != 0) 146219261079SEd Maste fatal_fr(r, "parse"); 146319261079SEd Maste 146419261079SEd Maste debug("request %u: lsetstat name \"%s\"", id, name); 146519261079SEd Maste if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { 146619261079SEd Maste /* nonsensical for links */ 146719261079SEd Maste status = SSH2_FX_BAD_MESSAGE; 146819261079SEd Maste goto out; 146919261079SEd Maste } 147019261079SEd Maste if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 147119261079SEd Maste logit("set \"%s\" mode %04o", name, a.perm); 147219261079SEd Maste r = fchmodat(AT_FDCWD, name, 147319261079SEd Maste a.perm & 07777, AT_SYMLINK_NOFOLLOW); 147419261079SEd Maste if (r == -1) 147519261079SEd Maste status = errno_to_portable(errno); 147619261079SEd Maste } 147719261079SEd Maste if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 147819261079SEd Maste char buf[64]; 147919261079SEd Maste time_t t = a.mtime; 148019261079SEd Maste 148119261079SEd Maste strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 148219261079SEd Maste localtime(&t)); 148319261079SEd Maste logit("set \"%s\" modtime %s", name, buf); 148419261079SEd Maste r = utimensat(AT_FDCWD, name, 148519261079SEd Maste attrib_to_ts(&a), AT_SYMLINK_NOFOLLOW); 148619261079SEd Maste if (r == -1) 148719261079SEd Maste status = errno_to_portable(errno); 148819261079SEd Maste } 148919261079SEd Maste if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { 149019261079SEd Maste logit("set \"%s\" owner %lu group %lu", name, 149119261079SEd Maste (u_long)a.uid, (u_long)a.gid); 149219261079SEd Maste r = fchownat(AT_FDCWD, name, a.uid, a.gid, 149319261079SEd Maste AT_SYMLINK_NOFOLLOW); 149419261079SEd Maste if (r == -1) 149519261079SEd Maste status = errno_to_portable(errno); 149619261079SEd Maste } 149719261079SEd Maste out: 149819261079SEd Maste send_status(id, status); 149919261079SEd Maste free(name); 150019261079SEd Maste } 150119261079SEd Maste 150219261079SEd Maste static void 150319261079SEd Maste process_extended_limits(u_int32_t id) 150419261079SEd Maste { 150519261079SEd Maste struct sshbuf *msg; 150619261079SEd Maste int r; 150719261079SEd Maste uint64_t nfiles = 0; 150819261079SEd Maste #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) 150919261079SEd Maste struct rlimit rlim; 151019261079SEd Maste #endif 151119261079SEd Maste 151219261079SEd Maste debug("request %u: limits", id); 151319261079SEd Maste 151419261079SEd Maste #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) 151519261079SEd Maste if (getrlimit(RLIMIT_NOFILE, &rlim) != -1 && rlim.rlim_cur > 5) 151619261079SEd Maste nfiles = rlim.rlim_cur - 5; /* stdio(3) + syslog + spare */ 151719261079SEd Maste #endif 151819261079SEd Maste 151919261079SEd Maste if ((msg = sshbuf_new()) == NULL) 152019261079SEd Maste fatal_f("sshbuf_new failed"); 152119261079SEd Maste if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED_REPLY)) != 0 || 152219261079SEd Maste (r = sshbuf_put_u32(msg, id)) != 0 || 152319261079SEd Maste /* max-packet-length */ 152419261079SEd Maste (r = sshbuf_put_u64(msg, SFTP_MAX_MSG_LENGTH)) != 0 || 152519261079SEd Maste /* max-read-length */ 152619261079SEd Maste (r = sshbuf_put_u64(msg, SFTP_MAX_READ_LENGTH)) != 0 || 152719261079SEd Maste /* max-write-length */ 152819261079SEd Maste (r = sshbuf_put_u64(msg, SFTP_MAX_MSG_LENGTH - 1024)) != 0 || 152919261079SEd Maste /* max-open-handles */ 153019261079SEd Maste (r = sshbuf_put_u64(msg, nfiles)) != 0) 153119261079SEd Maste fatal_fr(r, "compose"); 153219261079SEd Maste send_msg(msg); 153319261079SEd Maste sshbuf_free(msg); 153419261079SEd Maste } 153519261079SEd Maste 153619261079SEd Maste static void 153719261079SEd Maste process_extended_expand(u_int32_t id) 153819261079SEd Maste { 153919261079SEd Maste char cwd[PATH_MAX], resolvedname[PATH_MAX]; 154019261079SEd Maste char *path, *npath; 154119261079SEd Maste int r; 154219261079SEd Maste Stat s; 154319261079SEd Maste 154419261079SEd Maste if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) 154519261079SEd Maste fatal_fr(r, "parse"); 154619261079SEd Maste if (getcwd(cwd, sizeof(cwd)) == NULL) { 154719261079SEd Maste send_status(id, errno_to_portable(errno)); 154819261079SEd Maste goto out; 154919261079SEd Maste } 155019261079SEd Maste 155119261079SEd Maste debug3("request %u: expand, original \"%s\"", id, path); 155219261079SEd Maste if (path[0] == '\0') { 155319261079SEd Maste /* empty path */ 155419261079SEd Maste free(path); 155519261079SEd Maste path = xstrdup("."); 155619261079SEd Maste } else if (*path == '~') { 155719261079SEd Maste /* ~ expand path */ 155819261079SEd Maste /* Special-case for "~" and "~/" to respect homedir flag */ 155919261079SEd Maste if (strcmp(path, "~") == 0) { 156019261079SEd Maste free(path); 156119261079SEd Maste path = xstrdup(cwd); 156219261079SEd Maste } else if (strncmp(path, "~/", 2) == 0) { 156319261079SEd Maste npath = xstrdup(path + 2); 156419261079SEd Maste free(path); 156519261079SEd Maste xasprintf(&path, "%s/%s", cwd, npath); 1566*1323ec57SEd Maste free(npath); 156719261079SEd Maste } else { 156819261079SEd Maste /* ~user expansions */ 156919261079SEd Maste if (tilde_expand(path, pw->pw_uid, &npath) != 0) { 1570*1323ec57SEd Maste send_status_errmsg(id, 1571*1323ec57SEd Maste errno_to_portable(ENOENT), "no such user"); 157219261079SEd Maste goto out; 157319261079SEd Maste } 157419261079SEd Maste free(path); 157519261079SEd Maste path = npath; 157619261079SEd Maste } 157719261079SEd Maste } else if (*path != '/') { 157819261079SEd Maste /* relative path */ 157919261079SEd Maste xasprintf(&npath, "%s/%s", cwd, path); 158019261079SEd Maste free(path); 158119261079SEd Maste path = npath; 158219261079SEd Maste } 158319261079SEd Maste verbose("expand \"%s\"", path); 158419261079SEd Maste if (sftp_realpath(path, resolvedname) == NULL) { 158519261079SEd Maste send_status(id, errno_to_portable(errno)); 158619261079SEd Maste goto out; 158719261079SEd Maste } 158819261079SEd Maste attrib_clear(&s.attrib); 158919261079SEd Maste s.name = s.long_name = resolvedname; 159019261079SEd Maste send_names(id, 1, &s); 159119261079SEd Maste out: 159219261079SEd Maste free(path); 159319261079SEd Maste } 159419261079SEd Maste 159519261079SEd Maste static void 1596f7167e0eSDag-Erling Smørgrav process_extended(u_int32_t id) 1597f7167e0eSDag-Erling Smørgrav { 1598f7167e0eSDag-Erling Smørgrav char *request; 159919261079SEd Maste int r; 160019261079SEd Maste const struct sftp_handler *exthand; 1601f7167e0eSDag-Erling Smørgrav 1602bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_cstring(iqueue, &request, NULL)) != 0) 160319261079SEd Maste fatal_fr(r, "parse"); 160419261079SEd Maste if ((exthand = extended_handler_byname(request)) == NULL) { 1605f7167e0eSDag-Erling Smørgrav error("Unknown extended request \"%.100s\"", request); 16061e8db6e2SBrian Feldman send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 160719261079SEd Maste } else { 160819261079SEd Maste if (!request_permitted(exthand)) 160919261079SEd Maste send_status(id, SSH2_FX_PERMISSION_DENIED); 161019261079SEd Maste else 161119261079SEd Maste exthand->handler(id); 1612f7167e0eSDag-Erling Smørgrav } 1613e4a9863fSDag-Erling Smørgrav free(request); 16141e8db6e2SBrian Feldman } 1615b66f2d16SKris Kennaway 1616b66f2d16SKris Kennaway /* stolen from ssh-agent */ 1617b66f2d16SKris Kennaway 1618ae1f160dSDag-Erling Smørgrav static void 1619b66f2d16SKris Kennaway process(void) 1620b66f2d16SKris Kennaway { 1621bc5531deSDag-Erling Smørgrav u_int msg_len; 1622bc5531deSDag-Erling Smørgrav u_int buf_len; 1623bc5531deSDag-Erling Smørgrav u_int consumed; 1624bc5531deSDag-Erling Smørgrav u_char type; 1625bc5531deSDag-Erling Smørgrav const u_char *cp; 1626bc5531deSDag-Erling Smørgrav int i, r; 1627f7167e0eSDag-Erling Smørgrav u_int32_t id; 1628b66f2d16SKris Kennaway 1629bc5531deSDag-Erling Smørgrav buf_len = sshbuf_len(iqueue); 1630545d5ecaSDag-Erling Smørgrav if (buf_len < 5) 1631b66f2d16SKris Kennaway return; /* Incomplete message. */ 1632bc5531deSDag-Erling Smørgrav cp = sshbuf_ptr(iqueue); 1633761efaa7SDag-Erling Smørgrav msg_len = get_u32(cp); 1634021d409fSDag-Erling Smørgrav if (msg_len > SFTP_MAX_MSG_LENGTH) { 1635761efaa7SDag-Erling Smørgrav error("bad message from %s local user %s", 1636761efaa7SDag-Erling Smørgrav client_addr, pw->pw_name); 1637d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(11); 1638b66f2d16SKris Kennaway } 1639545d5ecaSDag-Erling Smørgrav if (buf_len < msg_len + 4) 1640b66f2d16SKris Kennaway return; 1641bc5531deSDag-Erling Smørgrav if ((r = sshbuf_consume(iqueue, 4)) != 0) 164219261079SEd Maste fatal_fr(r, "consume"); 1643545d5ecaSDag-Erling Smørgrav buf_len -= 4; 1644bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u8(iqueue, &type)) != 0) 164519261079SEd Maste fatal_fr(r, "parse type"); 1646f7167e0eSDag-Erling Smørgrav 1647b66f2d16SKris Kennaway switch (type) { 16481e8db6e2SBrian Feldman case SSH2_FXP_INIT: 1649b66f2d16SKris Kennaway process_init(); 1650f7167e0eSDag-Erling Smørgrav init_done = 1; 16511e8db6e2SBrian Feldman break; 16521e8db6e2SBrian Feldman case SSH2_FXP_EXTENDED: 1653f7167e0eSDag-Erling Smørgrav if (!init_done) 1654f7167e0eSDag-Erling Smørgrav fatal("Received extended request before init"); 1655bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(iqueue, &id)) != 0) 165619261079SEd Maste fatal_fr(r, "parse extended ID"); 1657f7167e0eSDag-Erling Smørgrav process_extended(id); 16581e8db6e2SBrian Feldman break; 1659b66f2d16SKris Kennaway default: 1660f7167e0eSDag-Erling Smørgrav if (!init_done) 1661f7167e0eSDag-Erling Smørgrav fatal("Received %u request before init", type); 1662bc5531deSDag-Erling Smørgrav if ((r = sshbuf_get_u32(iqueue, &id)) != 0) 166319261079SEd Maste fatal_fr(r, "parse ID"); 1664f7167e0eSDag-Erling Smørgrav for (i = 0; handlers[i].handler != NULL; i++) { 1665f7167e0eSDag-Erling Smørgrav if (type == handlers[i].type) { 1666f7167e0eSDag-Erling Smørgrav if (!request_permitted(&handlers[i])) { 1667f7167e0eSDag-Erling Smørgrav send_status(id, 1668f7167e0eSDag-Erling Smørgrav SSH2_FX_PERMISSION_DENIED); 1669f7167e0eSDag-Erling Smørgrav } else { 1670f7167e0eSDag-Erling Smørgrav handlers[i].handler(id); 1671f7167e0eSDag-Erling Smørgrav } 1672b66f2d16SKris Kennaway break; 1673b66f2d16SKris Kennaway } 1674f7167e0eSDag-Erling Smørgrav } 1675f7167e0eSDag-Erling Smørgrav if (handlers[i].handler == NULL) 1676f7167e0eSDag-Erling Smørgrav error("Unknown message %u", type); 1677f7167e0eSDag-Erling Smørgrav } 1678545d5ecaSDag-Erling Smørgrav /* discard the remaining bytes from the current packet */ 1679bc5531deSDag-Erling Smørgrav if (buf_len < sshbuf_len(iqueue)) { 1680d4af9e69SDag-Erling Smørgrav error("iqueue grew unexpectedly"); 1681d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1682d4af9e69SDag-Erling Smørgrav } 1683bc5531deSDag-Erling Smørgrav consumed = buf_len - sshbuf_len(iqueue); 1684d4af9e69SDag-Erling Smørgrav if (msg_len < consumed) { 1685f7167e0eSDag-Erling Smørgrav error("msg_len %u < consumed %u", msg_len, consumed); 1686d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1687d4af9e69SDag-Erling Smørgrav } 1688bc5531deSDag-Erling Smørgrav if (msg_len > consumed && 1689bc5531deSDag-Erling Smørgrav (r = sshbuf_consume(iqueue, msg_len - consumed)) != 0) 169019261079SEd Maste fatal_fr(r, "consume"); 1691b66f2d16SKris Kennaway } 1692b66f2d16SKris Kennaway 1693761efaa7SDag-Erling Smørgrav /* Cleanup handler that logs active handles upon normal exit */ 1694761efaa7SDag-Erling Smørgrav void 1695d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(int i) 1696761efaa7SDag-Erling Smørgrav { 1697761efaa7SDag-Erling Smørgrav if (pw != NULL && client_addr != NULL) { 1698761efaa7SDag-Erling Smørgrav handle_log_exit(); 1699761efaa7SDag-Erling Smørgrav logit("session closed for local user %s from [%s]", 1700761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr); 1701761efaa7SDag-Erling Smørgrav } 1702761efaa7SDag-Erling Smørgrav _exit(i); 1703761efaa7SDag-Erling Smørgrav } 1704761efaa7SDag-Erling Smørgrav 1705761efaa7SDag-Erling Smørgrav static void 1706d4af9e69SDag-Erling Smørgrav sftp_server_usage(void) 1707761efaa7SDag-Erling Smørgrav { 1708761efaa7SDag-Erling Smørgrav extern char *__progname; 1709761efaa7SDag-Erling Smørgrav 1710761efaa7SDag-Erling Smørgrav fprintf(stderr, 17116888a9beSDag-Erling Smørgrav "usage: %s [-ehR] [-d start_directory] [-f log_facility] " 171219261079SEd Maste "[-l log_level]\n\t[-P denied_requests] " 171319261079SEd Maste "[-p allowed_requests] [-u umask]\n" 1714f7167e0eSDag-Erling Smørgrav " %s -Q protocol_feature\n", 1715f7167e0eSDag-Erling Smørgrav __progname, __progname); 1716761efaa7SDag-Erling Smørgrav exit(1); 1717761efaa7SDag-Erling Smørgrav } 1718761efaa7SDag-Erling Smørgrav 1719b66f2d16SKris Kennaway int 1720d4af9e69SDag-Erling Smørgrav sftp_server_main(int argc, char **argv, struct passwd *user_pw) 1721b66f2d16SKris Kennaway { 1722*1323ec57SEd Maste int i, r, in, out, ch, skipargs = 0, log_stderr = 0; 1723*1323ec57SEd Maste ssize_t len, olen; 1724761efaa7SDag-Erling Smørgrav SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; 1725190cef3dSDag-Erling Smørgrav char *cp, *homedir = NULL, uidstr[32], buf[4*4096]; 17264a421b63SDag-Erling Smørgrav long mask; 1727761efaa7SDag-Erling Smørgrav 1728761efaa7SDag-Erling Smørgrav extern char *optarg; 1729761efaa7SDag-Erling Smørgrav extern char *__progname; 17301e8db6e2SBrian Feldman 1731761efaa7SDag-Erling Smørgrav __progname = ssh_get_progname(argv[0]); 1732761efaa7SDag-Erling Smørgrav log_init(__progname, log_level, log_facility, log_stderr); 1733b66f2d16SKris Kennaway 17346888a9beSDag-Erling Smørgrav pw = pwcopy(user_pw); 17356888a9beSDag-Erling Smørgrav 1736f7167e0eSDag-Erling Smørgrav while (!skipargs && (ch = getopt(argc, argv, 1737f7167e0eSDag-Erling Smørgrav "d:f:l:P:p:Q:u:cehR")) != -1) { 1738761efaa7SDag-Erling Smørgrav switch (ch) { 1739f7167e0eSDag-Erling Smørgrav case 'Q': 1740f7167e0eSDag-Erling Smørgrav if (strcasecmp(optarg, "requests") != 0) { 1741f7167e0eSDag-Erling Smørgrav fprintf(stderr, "Invalid query type\n"); 1742f7167e0eSDag-Erling Smørgrav exit(1); 1743f7167e0eSDag-Erling Smørgrav } 1744f7167e0eSDag-Erling Smørgrav for (i = 0; handlers[i].handler != NULL; i++) 1745f7167e0eSDag-Erling Smørgrav printf("%s\n", handlers[i].name); 1746f7167e0eSDag-Erling Smørgrav for (i = 0; extended_handlers[i].handler != NULL; i++) 1747f7167e0eSDag-Erling Smørgrav printf("%s\n", extended_handlers[i].name); 1748f7167e0eSDag-Erling Smørgrav exit(0); 1749f7167e0eSDag-Erling Smørgrav break; 1750b15c8340SDag-Erling Smørgrav case 'R': 1751b15c8340SDag-Erling Smørgrav readonly = 1; 1752b15c8340SDag-Erling Smørgrav break; 1753761efaa7SDag-Erling Smørgrav case 'c': 1754761efaa7SDag-Erling Smørgrav /* 1755761efaa7SDag-Erling Smørgrav * Ignore all arguments if we are invoked as a 1756761efaa7SDag-Erling Smørgrav * shell using "sftp-server -c command" 1757761efaa7SDag-Erling Smørgrav */ 1758761efaa7SDag-Erling Smørgrav skipargs = 1; 1759761efaa7SDag-Erling Smørgrav break; 1760761efaa7SDag-Erling Smørgrav case 'e': 1761761efaa7SDag-Erling Smørgrav log_stderr = 1; 1762761efaa7SDag-Erling Smørgrav break; 1763761efaa7SDag-Erling Smørgrav case 'l': 1764761efaa7SDag-Erling Smørgrav log_level = log_level_number(optarg); 1765761efaa7SDag-Erling Smørgrav if (log_level == SYSLOG_LEVEL_NOT_SET) 1766761efaa7SDag-Erling Smørgrav error("Invalid log level \"%s\"", optarg); 1767761efaa7SDag-Erling Smørgrav break; 1768761efaa7SDag-Erling Smørgrav case 'f': 1769761efaa7SDag-Erling Smørgrav log_facility = log_facility_number(optarg); 1770d4af9e69SDag-Erling Smørgrav if (log_facility == SYSLOG_FACILITY_NOT_SET) 1771761efaa7SDag-Erling Smørgrav error("Invalid log facility \"%s\"", optarg); 1772761efaa7SDag-Erling Smørgrav break; 17736888a9beSDag-Erling Smørgrav case 'd': 17746888a9beSDag-Erling Smørgrav cp = tilde_expand_filename(optarg, user_pw->pw_uid); 1775190cef3dSDag-Erling Smørgrav snprintf(uidstr, sizeof(uidstr), "%llu", 1776190cef3dSDag-Erling Smørgrav (unsigned long long)pw->pw_uid); 17776888a9beSDag-Erling Smørgrav homedir = percent_expand(cp, "d", user_pw->pw_dir, 1778190cef3dSDag-Erling Smørgrav "u", user_pw->pw_name, "U", uidstr, (char *)NULL); 17796888a9beSDag-Erling Smørgrav free(cp); 17806888a9beSDag-Erling Smørgrav break; 1781f7167e0eSDag-Erling Smørgrav case 'p': 178219261079SEd Maste if (request_allowlist != NULL) 1783f7167e0eSDag-Erling Smørgrav fatal("Permitted requests already set"); 178419261079SEd Maste request_allowlist = xstrdup(optarg); 1785f7167e0eSDag-Erling Smørgrav break; 1786f7167e0eSDag-Erling Smørgrav case 'P': 178719261079SEd Maste if (request_denylist != NULL) 1788f7167e0eSDag-Erling Smørgrav fatal("Refused requests already set"); 178919261079SEd Maste request_denylist = xstrdup(optarg); 1790f7167e0eSDag-Erling Smørgrav break; 1791b15c8340SDag-Erling Smørgrav case 'u': 17924a421b63SDag-Erling Smørgrav errno = 0; 17934a421b63SDag-Erling Smørgrav mask = strtol(optarg, &cp, 8); 17944a421b63SDag-Erling Smørgrav if (mask < 0 || mask > 0777 || *cp != '\0' || 17954a421b63SDag-Erling Smørgrav cp == optarg || (mask == 0 && errno != 0)) 17964a421b63SDag-Erling Smørgrav fatal("Invalid umask \"%s\"", optarg); 17974a421b63SDag-Erling Smørgrav (void)umask((mode_t)mask); 1798b15c8340SDag-Erling Smørgrav break; 1799761efaa7SDag-Erling Smørgrav case 'h': 1800761efaa7SDag-Erling Smørgrav default: 1801d4af9e69SDag-Erling Smørgrav sftp_server_usage(); 1802761efaa7SDag-Erling Smørgrav } 1803761efaa7SDag-Erling Smørgrav } 1804761efaa7SDag-Erling Smørgrav 1805761efaa7SDag-Erling Smørgrav log_init(__progname, log_level, log_facility, log_stderr); 1806761efaa7SDag-Erling Smørgrav 1807a0ee8cc6SDag-Erling Smørgrav /* 1808076ad2f8SDag-Erling Smørgrav * On platforms where we can, avoid making /proc/self/{mem,maps} 1809a0ee8cc6SDag-Erling Smørgrav * available to the user so that sftp access doesn't automatically 1810a0ee8cc6SDag-Erling Smørgrav * imply arbitrary code execution access that will break 1811a0ee8cc6SDag-Erling Smørgrav * restricted configurations. 1812a0ee8cc6SDag-Erling Smørgrav */ 1813076ad2f8SDag-Erling Smørgrav platform_disable_tracing(1); /* strict */ 1814a0ee8cc6SDag-Erling Smørgrav 1815acc1a9efSDag-Erling Smørgrav /* Drop any fine-grained privileges we don't need */ 1816acc1a9efSDag-Erling Smørgrav platform_pledge_sftp_server(); 1817acc1a9efSDag-Erling Smørgrav 1818761efaa7SDag-Erling Smørgrav if ((cp = getenv("SSH_CONNECTION")) != NULL) { 1819761efaa7SDag-Erling Smørgrav client_addr = xstrdup(cp); 1820d4af9e69SDag-Erling Smørgrav if ((cp = strchr(client_addr, ' ')) == NULL) { 1821d4af9e69SDag-Erling Smørgrav error("Malformed SSH_CONNECTION variable: \"%s\"", 1822761efaa7SDag-Erling Smørgrav getenv("SSH_CONNECTION")); 1823d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1824d4af9e69SDag-Erling Smørgrav } 1825761efaa7SDag-Erling Smørgrav *cp = '\0'; 1826761efaa7SDag-Erling Smørgrav } else 1827761efaa7SDag-Erling Smørgrav client_addr = xstrdup("UNKNOWN"); 1828761efaa7SDag-Erling Smørgrav 1829761efaa7SDag-Erling Smørgrav logit("session opened for local user %s from [%s]", 1830761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr); 1831761efaa7SDag-Erling Smørgrav 1832b15c8340SDag-Erling Smørgrav in = STDIN_FILENO; 1833b15c8340SDag-Erling Smørgrav out = STDOUT_FILENO; 1834b66f2d16SKris Kennaway 183583d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN 183683d2307dSDag-Erling Smørgrav setmode(in, O_BINARY); 183783d2307dSDag-Erling Smørgrav setmode(out, O_BINARY); 183883d2307dSDag-Erling Smørgrav #endif 183983d2307dSDag-Erling Smørgrav 1840bc5531deSDag-Erling Smørgrav if ((iqueue = sshbuf_new()) == NULL) 184119261079SEd Maste fatal_f("sshbuf_new failed"); 1842bc5531deSDag-Erling Smørgrav if ((oqueue = sshbuf_new()) == NULL) 184319261079SEd Maste fatal_f("sshbuf_new failed"); 1844b66f2d16SKris Kennaway 18456888a9beSDag-Erling Smørgrav if (homedir != NULL) { 18466888a9beSDag-Erling Smørgrav if (chdir(homedir) != 0) { 18476888a9beSDag-Erling Smørgrav error("chdir to \"%s\" failed: %s", homedir, 18486888a9beSDag-Erling Smørgrav strerror(errno)); 18496888a9beSDag-Erling Smørgrav } 18506888a9beSDag-Erling Smørgrav } 18516888a9beSDag-Erling Smørgrav 18521e8db6e2SBrian Feldman for (;;) { 1853*1323ec57SEd Maste struct pollfd pfd[2]; 1854*1323ec57SEd Maste 1855*1323ec57SEd Maste memset(pfd, 0, sizeof pfd); 1856*1323ec57SEd Maste pfd[0].fd = pfd[1].fd = -1; 18571e8db6e2SBrian Feldman 1858d4af9e69SDag-Erling Smørgrav /* 1859d4af9e69SDag-Erling Smørgrav * Ensure that we can read a full buffer and handle 1860d4af9e69SDag-Erling Smørgrav * the worst-case length packet it can generate, 1861d4af9e69SDag-Erling Smørgrav * otherwise apply backpressure by stopping reads. 1862d4af9e69SDag-Erling Smørgrav */ 1863bc5531deSDag-Erling Smørgrav if ((r = sshbuf_check_reserve(iqueue, sizeof(buf))) == 0 && 1864bc5531deSDag-Erling Smørgrav (r = sshbuf_check_reserve(oqueue, 1865*1323ec57SEd Maste SFTP_MAX_MSG_LENGTH)) == 0) { 1866*1323ec57SEd Maste pfd[0].fd = in; 1867*1323ec57SEd Maste pfd[0].events = POLLIN; 1868*1323ec57SEd Maste } 1869bc5531deSDag-Erling Smørgrav else if (r != SSH_ERR_NO_BUFFER_SPACE) 187019261079SEd Maste fatal_fr(r, "reserve"); 1871d4af9e69SDag-Erling Smørgrav 1872bc5531deSDag-Erling Smørgrav olen = sshbuf_len(oqueue); 1873*1323ec57SEd Maste if (olen > 0) { 1874*1323ec57SEd Maste pfd[1].fd = out; 1875*1323ec57SEd Maste pfd[1].events = POLLOUT; 1876*1323ec57SEd Maste } 1877b66f2d16SKris Kennaway 1878*1323ec57SEd Maste if (poll(pfd, 2, -1) == -1) { 1879b66f2d16SKris Kennaway if (errno == EINTR) 1880b66f2d16SKris Kennaway continue; 1881*1323ec57SEd Maste error("poll: %s", strerror(errno)); 1882d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(2); 1883b66f2d16SKris Kennaway } 1884b66f2d16SKris Kennaway 1885b66f2d16SKris Kennaway /* copy stdin to iqueue */ 1886*1323ec57SEd Maste if (pfd[0].revents & (POLLIN|POLLHUP)) { 1887b66f2d16SKris Kennaway len = read(in, buf, sizeof buf); 1888b66f2d16SKris Kennaway if (len == 0) { 1889b66f2d16SKris Kennaway debug("read eof"); 1890d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(0); 189119261079SEd Maste } else if (len == -1) { 1892*1323ec57SEd Maste if (errno != EAGAIN && errno != EINTR) { 1893761efaa7SDag-Erling Smørgrav error("read: %s", strerror(errno)); 1894d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1); 1895*1323ec57SEd Maste } 189619261079SEd Maste } else if ((r = sshbuf_put(iqueue, buf, len)) != 0) 189719261079SEd Maste fatal_fr(r, "sshbuf_put"); 1898b66f2d16SKris Kennaway } 1899b66f2d16SKris Kennaway /* send oqueue to stdout */ 1900*1323ec57SEd Maste if (pfd[1].revents & (POLLOUT|POLLHUP)) { 1901bc5531deSDag-Erling Smørgrav len = write(out, sshbuf_ptr(oqueue), olen); 1902*1323ec57SEd Maste if (len == 0 || (len == -1 && errno == EPIPE)) { 1903*1323ec57SEd Maste debug("write eof"); 1904*1323ec57SEd Maste sftp_server_cleanup_exit(0); 1905*1323ec57SEd Maste } else if (len == -1) { 1906*1323ec57SEd Maste sftp_server_cleanup_exit(1); 1907*1323ec57SEd Maste if (errno != EAGAIN && errno != EINTR) { 1908761efaa7SDag-Erling Smørgrav error("write: %s", strerror(errno)); 1909d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1); 1910*1323ec57SEd Maste } 191119261079SEd Maste } else if ((r = sshbuf_consume(oqueue, len)) != 0) 191219261079SEd Maste fatal_fr(r, "consume"); 1913b66f2d16SKris Kennaway } 1914d4af9e69SDag-Erling Smørgrav 1915d4af9e69SDag-Erling Smørgrav /* 1916d4af9e69SDag-Erling Smørgrav * Process requests from client if we can fit the results 1917d4af9e69SDag-Erling Smørgrav * into the output buffer, otherwise stop processing input 1918d4af9e69SDag-Erling Smørgrav * and let the output queue drain. 1919d4af9e69SDag-Erling Smørgrav */ 1920bc5531deSDag-Erling Smørgrav r = sshbuf_check_reserve(oqueue, SFTP_MAX_MSG_LENGTH); 1921bc5531deSDag-Erling Smørgrav if (r == 0) 1922b66f2d16SKris Kennaway process(); 1923bc5531deSDag-Erling Smørgrav else if (r != SSH_ERR_NO_BUFFER_SPACE) 192419261079SEd Maste fatal_fr(r, "reserve"); 1925b66f2d16SKris Kennaway } 1926b66f2d16SKris Kennaway } 1927