1f7167e0eSDag-Erling Smørgrav /* $OpenBSD: sftp-server.c,v 1.103 2014/01/17 06:23:24 dtucker Exp $ */ 2b66f2d16SKris Kennaway /* 3efcad6b7SDag-Erling Smørgrav * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. 4b66f2d16SKris Kennaway * 5efcad6b7SDag-Erling Smørgrav * Permission to use, copy, modify, and distribute this software for any 6efcad6b7SDag-Erling Smørgrav * purpose with or without fee is hereby granted, provided that the above 7efcad6b7SDag-Erling Smørgrav * copyright notice and this permission notice appear in all copies. 8b66f2d16SKris Kennaway * 9efcad6b7SDag-Erling Smørgrav * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10efcad6b7SDag-Erling Smørgrav * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11efcad6b7SDag-Erling Smørgrav * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12efcad6b7SDag-Erling Smørgrav * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13efcad6b7SDag-Erling Smørgrav * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14efcad6b7SDag-Erling Smørgrav * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15efcad6b7SDag-Erling Smørgrav * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16b66f2d16SKris Kennaway */ 17b66f2d16SKris Kennaway 18761efaa7SDag-Erling Smørgrav #include "includes.h" 19761efaa7SDag-Erling Smørgrav 20761efaa7SDag-Erling Smørgrav #include <sys/types.h> 21761efaa7SDag-Erling Smørgrav #include <sys/param.h> 22761efaa7SDag-Erling Smørgrav #include <sys/stat.h> 23761efaa7SDag-Erling Smørgrav #ifdef HAVE_SYS_TIME_H 24761efaa7SDag-Erling Smørgrav # include <sys/time.h> 25761efaa7SDag-Erling Smørgrav #endif 26d4af9e69SDag-Erling Smørgrav #ifdef HAVE_SYS_MOUNT_H 27d4af9e69SDag-Erling Smørgrav #include <sys/mount.h> 28d4af9e69SDag-Erling Smørgrav #endif 29d4af9e69SDag-Erling Smørgrav #ifdef HAVE_SYS_STATVFS_H 30d4af9e69SDag-Erling Smørgrav #include <sys/statvfs.h> 31d4af9e69SDag-Erling Smørgrav #endif 32*a0ee8cc6SDag-Erling Smørgrav #ifdef HAVE_SYS_PRCTL_H 33*a0ee8cc6SDag-Erling Smørgrav #include <sys/prctl.h> 34*a0ee8cc6SDag-Erling Smørgrav #endif 35761efaa7SDag-Erling Smørgrav 36761efaa7SDag-Erling Smørgrav #include <dirent.h> 37761efaa7SDag-Erling Smørgrav #include <errno.h> 38761efaa7SDag-Erling Smørgrav #include <fcntl.h> 39761efaa7SDag-Erling Smørgrav #include <pwd.h> 40761efaa7SDag-Erling Smørgrav #include <stdlib.h> 41761efaa7SDag-Erling Smørgrav #include <stdio.h> 42761efaa7SDag-Erling Smørgrav #include <string.h> 43761efaa7SDag-Erling Smørgrav #include <pwd.h> 44761efaa7SDag-Erling Smørgrav #include <time.h> 45761efaa7SDag-Erling Smørgrav #include <unistd.h> 46761efaa7SDag-Erling Smørgrav #include <stdarg.h> 47761efaa7SDag-Erling Smørgrav 48b66f2d16SKris Kennaway #include "xmalloc.h" 49761efaa7SDag-Erling Smørgrav #include "buffer.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 58b66f2d16SKris Kennaway /* helper */ 591e8db6e2SBrian Feldman #define get_int64() buffer_get_int64(&iqueue); 60b66f2d16SKris Kennaway #define get_int() buffer_get_int(&iqueue); 61b66f2d16SKris Kennaway #define get_string(lenp) buffer_get_string(&iqueue, lenp); 62b66f2d16SKris Kennaway 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 */ 71f7167e0eSDag-Erling Smørgrav static Buffer iqueue; 72f7167e0eSDag-Erling Smørgrav static Buffer 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 */ 84f7167e0eSDag-Erling Smørgrav static char *request_whitelist, *request_blacklist; 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); 119f7167e0eSDag-Erling Smørgrav static void process_extended(u_int32_t id); 120f7167e0eSDag-Erling Smørgrav 121f7167e0eSDag-Erling Smørgrav struct sftp_handler { 122f7167e0eSDag-Erling Smørgrav const char *name; /* user-visible name for fine-grained perms */ 123f7167e0eSDag-Erling Smørgrav const char *ext_name; /* extended request name */ 124f7167e0eSDag-Erling Smørgrav u_int type; /* packet type, for non extended packets */ 125f7167e0eSDag-Erling Smørgrav void (*handler)(u_int32_t); 126f7167e0eSDag-Erling Smørgrav int does_write; /* if nonzero, banned for readonly mode */ 127f7167e0eSDag-Erling Smørgrav }; 128f7167e0eSDag-Erling Smørgrav 129f7167e0eSDag-Erling Smørgrav struct sftp_handler handlers[] = { 130f7167e0eSDag-Erling Smørgrav /* NB. SSH2_FXP_OPEN does the readonly check in the handler itself */ 131f7167e0eSDag-Erling Smørgrav { "open", NULL, SSH2_FXP_OPEN, process_open, 0 }, 132f7167e0eSDag-Erling Smørgrav { "close", NULL, SSH2_FXP_CLOSE, process_close, 0 }, 133f7167e0eSDag-Erling Smørgrav { "read", NULL, SSH2_FXP_READ, process_read, 0 }, 134f7167e0eSDag-Erling Smørgrav { "write", NULL, SSH2_FXP_WRITE, process_write, 1 }, 135f7167e0eSDag-Erling Smørgrav { "lstat", NULL, SSH2_FXP_LSTAT, process_lstat, 0 }, 136f7167e0eSDag-Erling Smørgrav { "fstat", NULL, SSH2_FXP_FSTAT, process_fstat, 0 }, 137f7167e0eSDag-Erling Smørgrav { "setstat", NULL, SSH2_FXP_SETSTAT, process_setstat, 1 }, 138f7167e0eSDag-Erling Smørgrav { "fsetstat", NULL, SSH2_FXP_FSETSTAT, process_fsetstat, 1 }, 139f7167e0eSDag-Erling Smørgrav { "opendir", NULL, SSH2_FXP_OPENDIR, process_opendir, 0 }, 140f7167e0eSDag-Erling Smørgrav { "readdir", NULL, SSH2_FXP_READDIR, process_readdir, 0 }, 141f7167e0eSDag-Erling Smørgrav { "remove", NULL, SSH2_FXP_REMOVE, process_remove, 1 }, 142f7167e0eSDag-Erling Smørgrav { "mkdir", NULL, SSH2_FXP_MKDIR, process_mkdir, 1 }, 143f7167e0eSDag-Erling Smørgrav { "rmdir", NULL, SSH2_FXP_RMDIR, process_rmdir, 1 }, 144f7167e0eSDag-Erling Smørgrav { "realpath", NULL, SSH2_FXP_REALPATH, process_realpath, 0 }, 145f7167e0eSDag-Erling Smørgrav { "stat", NULL, SSH2_FXP_STAT, process_stat, 0 }, 146f7167e0eSDag-Erling Smørgrav { "rename", NULL, SSH2_FXP_RENAME, process_rename, 1 }, 147f7167e0eSDag-Erling Smørgrav { "readlink", NULL, SSH2_FXP_READLINK, process_readlink, 0 }, 148f7167e0eSDag-Erling Smørgrav { "symlink", NULL, SSH2_FXP_SYMLINK, process_symlink, 1 }, 149f7167e0eSDag-Erling Smørgrav { NULL, NULL, 0, NULL, 0 } 150f7167e0eSDag-Erling Smørgrav }; 151f7167e0eSDag-Erling Smørgrav 152f7167e0eSDag-Erling Smørgrav /* SSH2_FXP_EXTENDED submessages */ 153f7167e0eSDag-Erling Smørgrav struct sftp_handler extended_handlers[] = { 154f7167e0eSDag-Erling Smørgrav { "posix-rename", "posix-rename@openssh.com", 0, 155f7167e0eSDag-Erling Smørgrav process_extended_posix_rename, 1 }, 156f7167e0eSDag-Erling Smørgrav { "statvfs", "statvfs@openssh.com", 0, process_extended_statvfs, 0 }, 157f7167e0eSDag-Erling Smørgrav { "fstatvfs", "fstatvfs@openssh.com", 0, process_extended_fstatvfs, 0 }, 158f7167e0eSDag-Erling Smørgrav { "hardlink", "hardlink@openssh.com", 0, process_extended_hardlink, 1 }, 159f7167e0eSDag-Erling Smørgrav { "fsync", "fsync@openssh.com", 0, process_extended_fsync, 1 }, 160f7167e0eSDag-Erling Smørgrav { NULL, NULL, 0, NULL, 0 } 161f7167e0eSDag-Erling Smørgrav }; 162f7167e0eSDag-Erling Smørgrav 163f7167e0eSDag-Erling Smørgrav static int 164f7167e0eSDag-Erling Smørgrav request_permitted(struct sftp_handler *h) 165f7167e0eSDag-Erling Smørgrav { 166f7167e0eSDag-Erling Smørgrav char *result; 167f7167e0eSDag-Erling Smørgrav 168f7167e0eSDag-Erling Smørgrav if (readonly && h->does_write) { 169f7167e0eSDag-Erling Smørgrav verbose("Refusing %s request in read-only mode", h->name); 170f7167e0eSDag-Erling Smørgrav return 0; 171f7167e0eSDag-Erling Smørgrav } 172f7167e0eSDag-Erling Smørgrav if (request_blacklist != NULL && 173f7167e0eSDag-Erling Smørgrav ((result = match_list(h->name, request_blacklist, NULL))) != NULL) { 174f7167e0eSDag-Erling Smørgrav free(result); 175f7167e0eSDag-Erling Smørgrav verbose("Refusing blacklisted %s request", h->name); 176f7167e0eSDag-Erling Smørgrav return 0; 177f7167e0eSDag-Erling Smørgrav } 178f7167e0eSDag-Erling Smørgrav if (request_whitelist != NULL && 179f7167e0eSDag-Erling Smørgrav ((result = match_list(h->name, request_whitelist, NULL))) != NULL) { 180f7167e0eSDag-Erling Smørgrav free(result); 181f7167e0eSDag-Erling Smørgrav debug2("Permitting whitelisted %s request", h->name); 182f7167e0eSDag-Erling Smørgrav return 1; 183f7167e0eSDag-Erling Smørgrav } 184f7167e0eSDag-Erling Smørgrav if (request_whitelist != NULL) { 185f7167e0eSDag-Erling Smørgrav verbose("Refusing non-whitelisted %s request", h->name); 186f7167e0eSDag-Erling Smørgrav return 0; 187f7167e0eSDag-Erling Smørgrav } 188f7167e0eSDag-Erling Smørgrav return 1; 189f7167e0eSDag-Erling Smørgrav } 190f7167e0eSDag-Erling Smørgrav 191ae1f160dSDag-Erling Smørgrav static int 192b66f2d16SKris Kennaway errno_to_portable(int unixerrno) 193b66f2d16SKris Kennaway { 194b66f2d16SKris Kennaway int ret = 0; 1951e8db6e2SBrian Feldman 196b66f2d16SKris Kennaway switch (unixerrno) { 197b66f2d16SKris Kennaway case 0: 1981e8db6e2SBrian Feldman ret = SSH2_FX_OK; 199b66f2d16SKris Kennaway break; 200b66f2d16SKris Kennaway case ENOENT: 201b66f2d16SKris Kennaway case ENOTDIR: 202b66f2d16SKris Kennaway case EBADF: 203b66f2d16SKris Kennaway case ELOOP: 2041e8db6e2SBrian Feldman ret = SSH2_FX_NO_SUCH_FILE; 205b66f2d16SKris Kennaway break; 206b66f2d16SKris Kennaway case EPERM: 207b66f2d16SKris Kennaway case EACCES: 208b66f2d16SKris Kennaway case EFAULT: 2091e8db6e2SBrian Feldman ret = SSH2_FX_PERMISSION_DENIED; 210b66f2d16SKris Kennaway break; 211b66f2d16SKris Kennaway case ENAMETOOLONG: 212b66f2d16SKris Kennaway case EINVAL: 2131e8db6e2SBrian Feldman ret = SSH2_FX_BAD_MESSAGE; 214b66f2d16SKris Kennaway break; 215d4af9e69SDag-Erling Smørgrav case ENOSYS: 216d4af9e69SDag-Erling Smørgrav ret = SSH2_FX_OP_UNSUPPORTED; 217d4af9e69SDag-Erling Smørgrav break; 218b66f2d16SKris Kennaway default: 2191e8db6e2SBrian Feldman ret = SSH2_FX_FAILURE; 220b66f2d16SKris Kennaway break; 221b66f2d16SKris Kennaway } 222b66f2d16SKris Kennaway return ret; 223b66f2d16SKris Kennaway } 224b66f2d16SKris Kennaway 225ae1f160dSDag-Erling Smørgrav static int 226b66f2d16SKris Kennaway flags_from_portable(int pflags) 227b66f2d16SKris Kennaway { 228b66f2d16SKris Kennaway int flags = 0; 2291e8db6e2SBrian Feldman 2301e8db6e2SBrian Feldman if ((pflags & SSH2_FXF_READ) && 2311e8db6e2SBrian Feldman (pflags & SSH2_FXF_WRITE)) { 232b66f2d16SKris Kennaway flags = O_RDWR; 2331e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_READ) { 234b66f2d16SKris Kennaway flags = O_RDONLY; 2351e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_WRITE) { 236b66f2d16SKris Kennaway flags = O_WRONLY; 237b66f2d16SKris Kennaway } 238f7167e0eSDag-Erling Smørgrav if (pflags & SSH2_FXF_APPEND) 239f7167e0eSDag-Erling Smørgrav flags |= O_APPEND; 2401e8db6e2SBrian Feldman if (pflags & SSH2_FXF_CREAT) 241b66f2d16SKris Kennaway flags |= O_CREAT; 2421e8db6e2SBrian Feldman if (pflags & SSH2_FXF_TRUNC) 243b66f2d16SKris Kennaway flags |= O_TRUNC; 2441e8db6e2SBrian Feldman if (pflags & SSH2_FXF_EXCL) 245b66f2d16SKris Kennaway flags |= O_EXCL; 246b66f2d16SKris Kennaway return flags; 247b66f2d16SKris Kennaway } 248b66f2d16SKris Kennaway 249761efaa7SDag-Erling Smørgrav static const char * 250761efaa7SDag-Erling Smørgrav string_from_portable(int pflags) 251761efaa7SDag-Erling Smørgrav { 252761efaa7SDag-Erling Smørgrav static char ret[128]; 253761efaa7SDag-Erling Smørgrav 254761efaa7SDag-Erling Smørgrav *ret = '\0'; 255761efaa7SDag-Erling Smørgrav 256761efaa7SDag-Erling Smørgrav #define PAPPEND(str) { \ 257761efaa7SDag-Erling Smørgrav if (*ret != '\0') \ 258761efaa7SDag-Erling Smørgrav strlcat(ret, ",", sizeof(ret)); \ 259761efaa7SDag-Erling Smørgrav strlcat(ret, str, sizeof(ret)); \ 260761efaa7SDag-Erling Smørgrav } 261761efaa7SDag-Erling Smørgrav 262761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_READ) 263761efaa7SDag-Erling Smørgrav PAPPEND("READ") 264761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_WRITE) 265761efaa7SDag-Erling Smørgrav PAPPEND("WRITE") 266f7167e0eSDag-Erling Smørgrav if (pflags & SSH2_FXF_APPEND) 267f7167e0eSDag-Erling Smørgrav PAPPEND("APPEND") 268761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_CREAT) 269761efaa7SDag-Erling Smørgrav PAPPEND("CREATE") 270761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_TRUNC) 271761efaa7SDag-Erling Smørgrav PAPPEND("TRUNCATE") 272761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_EXCL) 273761efaa7SDag-Erling Smørgrav PAPPEND("EXCL") 274761efaa7SDag-Erling Smørgrav 275761efaa7SDag-Erling Smørgrav return ret; 276761efaa7SDag-Erling Smørgrav } 277761efaa7SDag-Erling Smørgrav 278ae1f160dSDag-Erling Smørgrav static Attrib * 279b66f2d16SKris Kennaway get_attrib(void) 280b66f2d16SKris Kennaway { 281b66f2d16SKris Kennaway return decode_attrib(&iqueue); 282b66f2d16SKris Kennaway } 283b66f2d16SKris Kennaway 284b66f2d16SKris Kennaway /* handle handles */ 285b66f2d16SKris Kennaway 286b66f2d16SKris Kennaway typedef struct Handle Handle; 287b66f2d16SKris Kennaway struct Handle { 288b66f2d16SKris Kennaway int use; 289b66f2d16SKris Kennaway DIR *dirp; 290b66f2d16SKris Kennaway int fd; 291f7167e0eSDag-Erling Smørgrav int flags; 292b66f2d16SKris Kennaway char *name; 293761efaa7SDag-Erling Smørgrav u_int64_t bytes_read, bytes_write; 294d4af9e69SDag-Erling Smørgrav int next_unused; 295b66f2d16SKris Kennaway }; 2961e8db6e2SBrian Feldman 297b66f2d16SKris Kennaway enum { 298b66f2d16SKris Kennaway HANDLE_UNUSED, 299b66f2d16SKris Kennaway HANDLE_DIR, 300b66f2d16SKris Kennaway HANDLE_FILE 301b66f2d16SKris Kennaway }; 3021e8db6e2SBrian Feldman 303d4af9e69SDag-Erling Smørgrav Handle *handles = NULL; 304d4af9e69SDag-Erling Smørgrav u_int num_handles = 0; 305d4af9e69SDag-Erling Smørgrav int first_unused_handle = -1; 306b66f2d16SKris Kennaway 307d4af9e69SDag-Erling Smørgrav static void handle_unused(int i) 308b66f2d16SKris Kennaway { 309b66f2d16SKris Kennaway handles[i].use = HANDLE_UNUSED; 310d4af9e69SDag-Erling Smørgrav handles[i].next_unused = first_unused_handle; 311d4af9e69SDag-Erling Smørgrav first_unused_handle = i; 312b66f2d16SKris Kennaway } 313b66f2d16SKris Kennaway 314ae1f160dSDag-Erling Smørgrav static int 315f7167e0eSDag-Erling Smørgrav handle_new(int use, const char *name, int fd, int flags, DIR *dirp) 316b66f2d16SKris Kennaway { 317d4af9e69SDag-Erling Smørgrav int i; 3181e8db6e2SBrian Feldman 319d4af9e69SDag-Erling Smørgrav if (first_unused_handle == -1) { 320d4af9e69SDag-Erling Smørgrav if (num_handles + 1 <= num_handles) 321d4af9e69SDag-Erling Smørgrav return -1; 322d4af9e69SDag-Erling Smørgrav num_handles++; 323d4af9e69SDag-Erling Smørgrav handles = xrealloc(handles, num_handles, sizeof(Handle)); 324d4af9e69SDag-Erling Smørgrav handle_unused(num_handles - 1); 325d4af9e69SDag-Erling Smørgrav } 326d4af9e69SDag-Erling Smørgrav 327d4af9e69SDag-Erling Smørgrav i = first_unused_handle; 328d4af9e69SDag-Erling Smørgrav first_unused_handle = handles[i].next_unused; 329d4af9e69SDag-Erling Smørgrav 330b66f2d16SKris Kennaway handles[i].use = use; 331b66f2d16SKris Kennaway handles[i].dirp = dirp; 332b66f2d16SKris Kennaway handles[i].fd = fd; 333f7167e0eSDag-Erling Smørgrav handles[i].flags = flags; 334d0c8c0bcSDag-Erling Smørgrav handles[i].name = xstrdup(name); 335761efaa7SDag-Erling Smørgrav handles[i].bytes_read = handles[i].bytes_write = 0; 336d4af9e69SDag-Erling Smørgrav 337b66f2d16SKris Kennaway return i; 338b66f2d16SKris Kennaway } 339b66f2d16SKris Kennaway 340ae1f160dSDag-Erling Smørgrav static int 341b66f2d16SKris Kennaway handle_is_ok(int i, int type) 342b66f2d16SKris Kennaway { 343d4af9e69SDag-Erling Smørgrav return i >= 0 && (u_int)i < num_handles && handles[i].use == type; 344b66f2d16SKris Kennaway } 345b66f2d16SKris Kennaway 346ae1f160dSDag-Erling Smørgrav static int 347b66f2d16SKris Kennaway handle_to_string(int handle, char **stringp, int *hlenp) 348b66f2d16SKris Kennaway { 349b66f2d16SKris Kennaway if (stringp == NULL || hlenp == NULL) 350b66f2d16SKris Kennaway return -1; 3511e8db6e2SBrian Feldman *stringp = xmalloc(sizeof(int32_t)); 352761efaa7SDag-Erling Smørgrav put_u32(*stringp, handle); 3531e8db6e2SBrian Feldman *hlenp = sizeof(int32_t); 354b66f2d16SKris Kennaway return 0; 355b66f2d16SKris Kennaway } 356b66f2d16SKris Kennaway 357ae1f160dSDag-Erling Smørgrav static int 358efcad6b7SDag-Erling Smørgrav handle_from_string(const char *handle, u_int hlen) 359b66f2d16SKris Kennaway { 3601e8db6e2SBrian Feldman int val; 3611e8db6e2SBrian Feldman 3621e8db6e2SBrian Feldman if (hlen != sizeof(int32_t)) 363b66f2d16SKris Kennaway return -1; 364761efaa7SDag-Erling Smørgrav val = get_u32(handle); 365b66f2d16SKris Kennaway if (handle_is_ok(val, HANDLE_FILE) || 366b66f2d16SKris Kennaway handle_is_ok(val, HANDLE_DIR)) 367b66f2d16SKris Kennaway return val; 368b66f2d16SKris Kennaway return -1; 369b66f2d16SKris Kennaway } 370b66f2d16SKris Kennaway 371ae1f160dSDag-Erling Smørgrav static char * 372b66f2d16SKris Kennaway handle_to_name(int handle) 373b66f2d16SKris Kennaway { 374b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)|| 375b66f2d16SKris Kennaway handle_is_ok(handle, HANDLE_FILE)) 376b66f2d16SKris Kennaway return handles[handle].name; 377b66f2d16SKris Kennaway return NULL; 378b66f2d16SKris Kennaway } 379b66f2d16SKris Kennaway 380ae1f160dSDag-Erling Smørgrav static DIR * 381b66f2d16SKris Kennaway handle_to_dir(int handle) 382b66f2d16SKris Kennaway { 383b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)) 384b66f2d16SKris Kennaway return handles[handle].dirp; 385b66f2d16SKris Kennaway return NULL; 386b66f2d16SKris Kennaway } 387b66f2d16SKris Kennaway 388ae1f160dSDag-Erling Smørgrav static int 389b66f2d16SKris Kennaway handle_to_fd(int handle) 390b66f2d16SKris Kennaway { 391b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) 392b66f2d16SKris Kennaway return handles[handle].fd; 393b66f2d16SKris Kennaway return -1; 394b66f2d16SKris Kennaway } 395b66f2d16SKris Kennaway 396f7167e0eSDag-Erling Smørgrav static int 397f7167e0eSDag-Erling Smørgrav handle_to_flags(int handle) 398f7167e0eSDag-Erling Smørgrav { 399f7167e0eSDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 400f7167e0eSDag-Erling Smørgrav return handles[handle].flags; 401f7167e0eSDag-Erling Smørgrav return 0; 402f7167e0eSDag-Erling Smørgrav } 403f7167e0eSDag-Erling Smørgrav 404761efaa7SDag-Erling Smørgrav static void 405761efaa7SDag-Erling Smørgrav handle_update_read(int handle, ssize_t bytes) 406761efaa7SDag-Erling Smørgrav { 407761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 408761efaa7SDag-Erling Smørgrav handles[handle].bytes_read += bytes; 409761efaa7SDag-Erling Smørgrav } 410761efaa7SDag-Erling Smørgrav 411761efaa7SDag-Erling Smørgrav static void 412761efaa7SDag-Erling Smørgrav handle_update_write(int handle, ssize_t bytes) 413761efaa7SDag-Erling Smørgrav { 414761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 415761efaa7SDag-Erling Smørgrav handles[handle].bytes_write += bytes; 416761efaa7SDag-Erling Smørgrav } 417761efaa7SDag-Erling Smørgrav 418761efaa7SDag-Erling Smørgrav static u_int64_t 419761efaa7SDag-Erling Smørgrav handle_bytes_read(int handle) 420761efaa7SDag-Erling Smørgrav { 421761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 422761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_read); 423761efaa7SDag-Erling Smørgrav return 0; 424761efaa7SDag-Erling Smørgrav } 425761efaa7SDag-Erling Smørgrav 426761efaa7SDag-Erling Smørgrav static u_int64_t 427761efaa7SDag-Erling Smørgrav handle_bytes_write(int handle) 428761efaa7SDag-Erling Smørgrav { 429761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 430761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_write); 431761efaa7SDag-Erling Smørgrav return 0; 432761efaa7SDag-Erling Smørgrav } 433761efaa7SDag-Erling Smørgrav 434ae1f160dSDag-Erling Smørgrav static int 435b66f2d16SKris Kennaway handle_close(int handle) 436b66f2d16SKris Kennaway { 437b66f2d16SKris Kennaway int ret = -1; 4381e8db6e2SBrian Feldman 439b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) { 440b66f2d16SKris Kennaway ret = close(handles[handle].fd); 441e4a9863fSDag-Erling Smørgrav free(handles[handle].name); 442d4af9e69SDag-Erling Smørgrav handle_unused(handle); 443b66f2d16SKris Kennaway } else if (handle_is_ok(handle, HANDLE_DIR)) { 444b66f2d16SKris Kennaway ret = closedir(handles[handle].dirp); 445e4a9863fSDag-Erling Smørgrav free(handles[handle].name); 446d4af9e69SDag-Erling Smørgrav handle_unused(handle); 447b66f2d16SKris Kennaway } else { 448b66f2d16SKris Kennaway errno = ENOENT; 449b66f2d16SKris Kennaway } 450b66f2d16SKris Kennaway return ret; 451b66f2d16SKris Kennaway } 452b66f2d16SKris Kennaway 453761efaa7SDag-Erling Smørgrav static void 454761efaa7SDag-Erling Smørgrav handle_log_close(int handle, char *emsg) 455761efaa7SDag-Erling Smørgrav { 456761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) { 457761efaa7SDag-Erling Smørgrav logit("%s%sclose \"%s\" bytes read %llu written %llu", 458761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 459761efaa7SDag-Erling Smørgrav handle_to_name(handle), 460d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_read(handle), 461d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_write(handle)); 462761efaa7SDag-Erling Smørgrav } else { 463761efaa7SDag-Erling Smørgrav logit("%s%sclosedir \"%s\"", 464761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 465761efaa7SDag-Erling Smørgrav handle_to_name(handle)); 466761efaa7SDag-Erling Smørgrav } 467761efaa7SDag-Erling Smørgrav } 468761efaa7SDag-Erling Smørgrav 469761efaa7SDag-Erling Smørgrav static void 470761efaa7SDag-Erling Smørgrav handle_log_exit(void) 471761efaa7SDag-Erling Smørgrav { 472761efaa7SDag-Erling Smørgrav u_int i; 473761efaa7SDag-Erling Smørgrav 474d4af9e69SDag-Erling Smørgrav for (i = 0; i < num_handles; i++) 475761efaa7SDag-Erling Smørgrav if (handles[i].use != HANDLE_UNUSED) 476761efaa7SDag-Erling Smørgrav handle_log_close(i, "forced"); 477761efaa7SDag-Erling Smørgrav } 478761efaa7SDag-Erling Smørgrav 479ae1f160dSDag-Erling Smørgrav static int 480b66f2d16SKris Kennaway get_handle(void) 481b66f2d16SKris Kennaway { 482b66f2d16SKris Kennaway char *handle; 4831e8db6e2SBrian Feldman int val = -1; 484b66f2d16SKris Kennaway u_int hlen; 4851e8db6e2SBrian Feldman 486b66f2d16SKris Kennaway handle = get_string(&hlen); 4871e8db6e2SBrian Feldman if (hlen < 256) 488b66f2d16SKris Kennaway val = handle_from_string(handle, hlen); 489e4a9863fSDag-Erling Smørgrav free(handle); 490b66f2d16SKris Kennaway return val; 491b66f2d16SKris Kennaway } 492b66f2d16SKris Kennaway 493b66f2d16SKris Kennaway /* send replies */ 494b66f2d16SKris Kennaway 495ae1f160dSDag-Erling Smørgrav static void 496b66f2d16SKris Kennaway send_msg(Buffer *m) 497b66f2d16SKris Kennaway { 498b66f2d16SKris Kennaway int mlen = buffer_len(m); 4991e8db6e2SBrian Feldman 500b66f2d16SKris Kennaway buffer_put_int(&oqueue, mlen); 501b66f2d16SKris Kennaway buffer_append(&oqueue, buffer_ptr(m), mlen); 502b66f2d16SKris Kennaway buffer_consume(m, mlen); 503b66f2d16SKris Kennaway } 504b66f2d16SKris Kennaway 505761efaa7SDag-Erling Smørgrav static const char * 506761efaa7SDag-Erling Smørgrav status_to_message(u_int32_t status) 507b66f2d16SKris Kennaway { 5081e8db6e2SBrian Feldman const char *status_messages[] = { 5091e8db6e2SBrian Feldman "Success", /* SSH_FX_OK */ 5101e8db6e2SBrian Feldman "End of file", /* SSH_FX_EOF */ 5111e8db6e2SBrian Feldman "No such file", /* SSH_FX_NO_SUCH_FILE */ 5121e8db6e2SBrian Feldman "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 5131e8db6e2SBrian Feldman "Failure", /* SSH_FX_FAILURE */ 5141e8db6e2SBrian Feldman "Bad message", /* SSH_FX_BAD_MESSAGE */ 5151e8db6e2SBrian Feldman "No connection", /* SSH_FX_NO_CONNECTION */ 5161e8db6e2SBrian Feldman "Connection lost", /* SSH_FX_CONNECTION_LOST */ 5171e8db6e2SBrian Feldman "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 5181e8db6e2SBrian Feldman "Unknown error" /* Others */ 5191e8db6e2SBrian Feldman }; 520761efaa7SDag-Erling Smørgrav return (status_messages[MIN(status,SSH2_FX_MAX)]); 521761efaa7SDag-Erling Smørgrav } 5221e8db6e2SBrian Feldman 523761efaa7SDag-Erling Smørgrav static void 524761efaa7SDag-Erling Smørgrav send_status(u_int32_t id, u_int32_t status) 525761efaa7SDag-Erling Smørgrav { 526761efaa7SDag-Erling Smørgrav Buffer msg; 527761efaa7SDag-Erling Smørgrav 528761efaa7SDag-Erling Smørgrav debug3("request %u: sent status %u", id, status); 529761efaa7SDag-Erling Smørgrav if (log_level > SYSLOG_LEVEL_VERBOSE || 530761efaa7SDag-Erling Smørgrav (status != SSH2_FX_OK && status != SSH2_FX_EOF)) 531761efaa7SDag-Erling Smørgrav logit("sent status %s", status_to_message(status)); 532b66f2d16SKris Kennaway buffer_init(&msg); 5331e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_STATUS); 534b66f2d16SKris Kennaway buffer_put_int(&msg, id); 535d74d50a8SDag-Erling Smørgrav buffer_put_int(&msg, status); 5361e8db6e2SBrian Feldman if (version >= 3) { 537761efaa7SDag-Erling Smørgrav buffer_put_cstring(&msg, status_to_message(status)); 5381e8db6e2SBrian Feldman buffer_put_cstring(&msg, ""); 5391e8db6e2SBrian Feldman } 540b66f2d16SKris Kennaway send_msg(&msg); 541b66f2d16SKris Kennaway buffer_free(&msg); 542b66f2d16SKris Kennaway } 543ae1f160dSDag-Erling Smørgrav static void 544efcad6b7SDag-Erling Smørgrav send_data_or_handle(char type, u_int32_t id, const char *data, int dlen) 545b66f2d16SKris Kennaway { 546b66f2d16SKris Kennaway Buffer msg; 5471e8db6e2SBrian Feldman 548b66f2d16SKris Kennaway buffer_init(&msg); 549b66f2d16SKris Kennaway buffer_put_char(&msg, type); 550b66f2d16SKris Kennaway buffer_put_int(&msg, id); 551b66f2d16SKris Kennaway buffer_put_string(&msg, data, dlen); 552b66f2d16SKris Kennaway send_msg(&msg); 553b66f2d16SKris Kennaway buffer_free(&msg); 554b66f2d16SKris Kennaway } 555b66f2d16SKris Kennaway 556ae1f160dSDag-Erling Smørgrav static void 557efcad6b7SDag-Erling Smørgrav send_data(u_int32_t id, const char *data, int dlen) 558b66f2d16SKris Kennaway { 559761efaa7SDag-Erling Smørgrav debug("request %u: sent data len %d", id, dlen); 5601e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 561b66f2d16SKris Kennaway } 562b66f2d16SKris Kennaway 563ae1f160dSDag-Erling Smørgrav static void 564b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle) 565b66f2d16SKris Kennaway { 566b66f2d16SKris Kennaway char *string; 567b66f2d16SKris Kennaway int hlen; 5681e8db6e2SBrian Feldman 569b66f2d16SKris Kennaway handle_to_string(handle, &string, &hlen); 570761efaa7SDag-Erling Smørgrav debug("request %u: sent handle handle %d", id, handle); 5711e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 572e4a9863fSDag-Erling Smørgrav free(string); 573b66f2d16SKris Kennaway } 574b66f2d16SKris Kennaway 575ae1f160dSDag-Erling Smørgrav static void 576efcad6b7SDag-Erling Smørgrav send_names(u_int32_t id, int count, const Stat *stats) 577b66f2d16SKris Kennaway { 578b66f2d16SKris Kennaway Buffer msg; 579b66f2d16SKris Kennaway int i; 5801e8db6e2SBrian Feldman 581b66f2d16SKris Kennaway buffer_init(&msg); 5821e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_NAME); 583b66f2d16SKris Kennaway buffer_put_int(&msg, id); 584b66f2d16SKris Kennaway buffer_put_int(&msg, count); 585761efaa7SDag-Erling Smørgrav debug("request %u: sent names count %d", id, count); 586b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 587b66f2d16SKris Kennaway buffer_put_cstring(&msg, stats[i].name); 588b66f2d16SKris Kennaway buffer_put_cstring(&msg, stats[i].long_name); 589b66f2d16SKris Kennaway encode_attrib(&msg, &stats[i].attrib); 590b66f2d16SKris Kennaway } 591b66f2d16SKris Kennaway send_msg(&msg); 592b66f2d16SKris Kennaway buffer_free(&msg); 593b66f2d16SKris Kennaway } 594b66f2d16SKris Kennaway 595ae1f160dSDag-Erling Smørgrav static void 596efcad6b7SDag-Erling Smørgrav send_attrib(u_int32_t id, const Attrib *a) 597b66f2d16SKris Kennaway { 598b66f2d16SKris Kennaway Buffer msg; 5991e8db6e2SBrian Feldman 600761efaa7SDag-Erling Smørgrav debug("request %u: sent attrib have 0x%x", id, a->flags); 601b66f2d16SKris Kennaway buffer_init(&msg); 6021e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_ATTRS); 603b66f2d16SKris Kennaway buffer_put_int(&msg, id); 604b66f2d16SKris Kennaway encode_attrib(&msg, a); 605b66f2d16SKris Kennaway send_msg(&msg); 606b66f2d16SKris Kennaway buffer_free(&msg); 607b66f2d16SKris Kennaway } 608b66f2d16SKris Kennaway 609d4af9e69SDag-Erling Smørgrav static void 610d4af9e69SDag-Erling Smørgrav send_statvfs(u_int32_t id, struct statvfs *st) 611d4af9e69SDag-Erling Smørgrav { 612d4af9e69SDag-Erling Smørgrav Buffer msg; 613d4af9e69SDag-Erling Smørgrav u_int64_t flag; 614d4af9e69SDag-Erling Smørgrav 615d4af9e69SDag-Erling Smørgrav flag = (st->f_flag & ST_RDONLY) ? SSH2_FXE_STATVFS_ST_RDONLY : 0; 616d4af9e69SDag-Erling Smørgrav flag |= (st->f_flag & ST_NOSUID) ? SSH2_FXE_STATVFS_ST_NOSUID : 0; 617d4af9e69SDag-Erling Smørgrav 618d4af9e69SDag-Erling Smørgrav buffer_init(&msg); 619d4af9e69SDag-Erling Smørgrav buffer_put_char(&msg, SSH2_FXP_EXTENDED_REPLY); 620d4af9e69SDag-Erling Smørgrav buffer_put_int(&msg, id); 621d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_bsize); 622d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_frsize); 623d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_blocks); 624d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_bfree); 625d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_bavail); 626d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_files); 627d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_ffree); 628d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_favail); 629d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, FSID_TO_ULONG(st->f_fsid)); 630d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, flag); 631d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_namemax); 632d4af9e69SDag-Erling Smørgrav send_msg(&msg); 633d4af9e69SDag-Erling Smørgrav buffer_free(&msg); 634d4af9e69SDag-Erling Smørgrav } 635d4af9e69SDag-Erling Smørgrav 636b66f2d16SKris Kennaway /* parse incoming */ 637b66f2d16SKris Kennaway 638ae1f160dSDag-Erling Smørgrav static void 639b66f2d16SKris Kennaway process_init(void) 640b66f2d16SKris Kennaway { 641b66f2d16SKris Kennaway Buffer msg; 642b66f2d16SKris Kennaway 643545d5ecaSDag-Erling Smørgrav version = get_int(); 644e146993eSDag-Erling Smørgrav verbose("received client version %u", version); 645b66f2d16SKris Kennaway buffer_init(&msg); 6461e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_VERSION); 6471e8db6e2SBrian Feldman buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 648d4af9e69SDag-Erling Smørgrav /* POSIX rename extension */ 649d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "posix-rename@openssh.com"); 650d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "1"); /* version */ 651d4af9e69SDag-Erling Smørgrav /* statvfs extension */ 652d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "statvfs@openssh.com"); 653d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "2"); /* version */ 654d4af9e69SDag-Erling Smørgrav /* fstatvfs extension */ 655d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "fstatvfs@openssh.com"); 656d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "2"); /* version */ 6574a421b63SDag-Erling Smørgrav /* hardlink extension */ 6584a421b63SDag-Erling Smørgrav buffer_put_cstring(&msg, "hardlink@openssh.com"); 6594a421b63SDag-Erling Smørgrav buffer_put_cstring(&msg, "1"); /* version */ 660f7167e0eSDag-Erling Smørgrav /* fsync extension */ 661f7167e0eSDag-Erling Smørgrav buffer_put_cstring(&msg, "fsync@openssh.com"); 662f7167e0eSDag-Erling Smørgrav buffer_put_cstring(&msg, "1"); /* version */ 663b66f2d16SKris Kennaway send_msg(&msg); 664b66f2d16SKris Kennaway buffer_free(&msg); 665b66f2d16SKris Kennaway } 666b66f2d16SKris Kennaway 667ae1f160dSDag-Erling Smørgrav static void 668f7167e0eSDag-Erling Smørgrav process_open(u_int32_t id) 669b66f2d16SKris Kennaway { 670f7167e0eSDag-Erling Smørgrav u_int32_t pflags; 671b66f2d16SKris Kennaway Attrib *a; 672b66f2d16SKris Kennaway char *name; 6731e8db6e2SBrian Feldman int handle, fd, flags, mode, status = SSH2_FX_FAILURE; 674b66f2d16SKris Kennaway 675b66f2d16SKris Kennaway name = get_string(NULL); 6761e8db6e2SBrian Feldman pflags = get_int(); /* portable flags */ 677761efaa7SDag-Erling Smørgrav debug3("request %u: open flags %d", id, pflags); 678b66f2d16SKris Kennaway a = get_attrib(); 679b66f2d16SKris Kennaway flags = flags_from_portable(pflags); 6801e8db6e2SBrian Feldman mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; 681761efaa7SDag-Erling Smørgrav logit("open \"%s\" flags %s mode 0%o", 682761efaa7SDag-Erling Smørgrav name, string_from_portable(pflags), mode); 683b15c8340SDag-Erling Smørgrav if (readonly && 684f7167e0eSDag-Erling Smørgrav ((flags & O_ACCMODE) == O_WRONLY || 685f7167e0eSDag-Erling Smørgrav (flags & O_ACCMODE) == O_RDWR)) { 686f7167e0eSDag-Erling Smørgrav verbose("Refusing open request in read-only mode"); 687b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 688f7167e0eSDag-Erling Smørgrav } else { 689b66f2d16SKris Kennaway fd = open(name, flags, mode); 690b66f2d16SKris Kennaway if (fd < 0) { 691b66f2d16SKris Kennaway status = errno_to_portable(errno); 692b66f2d16SKris Kennaway } else { 693f7167e0eSDag-Erling Smørgrav handle = handle_new(HANDLE_FILE, name, fd, flags, NULL); 694b66f2d16SKris Kennaway if (handle < 0) { 695b66f2d16SKris Kennaway close(fd); 696b66f2d16SKris Kennaway } else { 697b66f2d16SKris Kennaway send_handle(id, handle); 6981e8db6e2SBrian Feldman status = SSH2_FX_OK; 699b66f2d16SKris Kennaway } 700b66f2d16SKris Kennaway } 701b15c8340SDag-Erling Smørgrav } 7021e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 703b66f2d16SKris Kennaway send_status(id, status); 704e4a9863fSDag-Erling Smørgrav free(name); 705b66f2d16SKris Kennaway } 706b66f2d16SKris Kennaway 707ae1f160dSDag-Erling Smørgrav static void 708f7167e0eSDag-Erling Smørgrav process_close(u_int32_t id) 709b66f2d16SKris Kennaway { 7101e8db6e2SBrian Feldman int handle, ret, status = SSH2_FX_FAILURE; 711b66f2d16SKris Kennaway 712b66f2d16SKris Kennaway handle = get_handle(); 713761efaa7SDag-Erling Smørgrav debug3("request %u: close handle %u", id, handle); 714761efaa7SDag-Erling Smørgrav handle_log_close(handle, NULL); 715b66f2d16SKris Kennaway ret = handle_close(handle); 7161e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 717b66f2d16SKris Kennaway send_status(id, status); 718b66f2d16SKris Kennaway } 719b66f2d16SKris Kennaway 720ae1f160dSDag-Erling Smørgrav static void 721f7167e0eSDag-Erling Smørgrav process_read(u_int32_t id) 722b66f2d16SKris Kennaway { 723b66f2d16SKris Kennaway char buf[64*1024]; 724f7167e0eSDag-Erling Smørgrav u_int32_t len; 7251e8db6e2SBrian Feldman int handle, fd, ret, status = SSH2_FX_FAILURE; 726b66f2d16SKris Kennaway u_int64_t off; 727b66f2d16SKris Kennaway 728b66f2d16SKris Kennaway handle = get_handle(); 7291e8db6e2SBrian Feldman off = get_int64(); 730b66f2d16SKris Kennaway len = get_int(); 731b66f2d16SKris Kennaway 732761efaa7SDag-Erling Smørgrav debug("request %u: read \"%s\" (handle %d) off %llu len %d", 733761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len); 734b66f2d16SKris Kennaway if (len > sizeof buf) { 735b66f2d16SKris Kennaway len = sizeof buf; 736761efaa7SDag-Erling Smørgrav debug2("read change len %d", len); 737b66f2d16SKris Kennaway } 738b66f2d16SKris Kennaway fd = handle_to_fd(handle); 739b66f2d16SKris Kennaway if (fd >= 0) { 740b66f2d16SKris Kennaway if (lseek(fd, off, SEEK_SET) < 0) { 741b66f2d16SKris Kennaway error("process_read: seek failed"); 742b66f2d16SKris Kennaway status = errno_to_portable(errno); 743b66f2d16SKris Kennaway } else { 744b66f2d16SKris Kennaway ret = read(fd, buf, len); 745b66f2d16SKris Kennaway if (ret < 0) { 746b66f2d16SKris Kennaway status = errno_to_portable(errno); 747b66f2d16SKris Kennaway } else if (ret == 0) { 7481e8db6e2SBrian Feldman status = SSH2_FX_EOF; 749b66f2d16SKris Kennaway } else { 750b66f2d16SKris Kennaway send_data(id, buf, ret); 7511e8db6e2SBrian Feldman status = SSH2_FX_OK; 752761efaa7SDag-Erling Smørgrav handle_update_read(handle, ret); 753b66f2d16SKris Kennaway } 754b66f2d16SKris Kennaway } 755b66f2d16SKris Kennaway } 7561e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 757b66f2d16SKris Kennaway send_status(id, status); 758b66f2d16SKris Kennaway } 759b66f2d16SKris Kennaway 760ae1f160dSDag-Erling Smørgrav static void 761f7167e0eSDag-Erling Smørgrav process_write(u_int32_t id) 762b66f2d16SKris Kennaway { 763b66f2d16SKris Kennaway u_int64_t off; 764b66f2d16SKris Kennaway u_int len; 765b15c8340SDag-Erling Smørgrav int handle, fd, ret, status; 766b66f2d16SKris Kennaway char *data; 767b66f2d16SKris Kennaway 768b66f2d16SKris Kennaway handle = get_handle(); 7691e8db6e2SBrian Feldman off = get_int64(); 770b66f2d16SKris Kennaway data = get_string(&len); 771b66f2d16SKris Kennaway 772761efaa7SDag-Erling Smørgrav debug("request %u: write \"%s\" (handle %d) off %llu len %d", 773761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len); 774b66f2d16SKris Kennaway fd = handle_to_fd(handle); 775b15c8340SDag-Erling Smørgrav 776b15c8340SDag-Erling Smørgrav if (fd < 0) 777b15c8340SDag-Erling Smørgrav status = SSH2_FX_FAILURE; 778b15c8340SDag-Erling Smørgrav else { 779f7167e0eSDag-Erling Smørgrav if (!(handle_to_flags(handle) & O_APPEND) && 780f7167e0eSDag-Erling Smørgrav lseek(fd, off, SEEK_SET) < 0) { 781b66f2d16SKris Kennaway status = errno_to_portable(errno); 782b66f2d16SKris Kennaway error("process_write: seek failed"); 783b66f2d16SKris Kennaway } else { 784b66f2d16SKris Kennaway /* XXX ATOMICIO ? */ 785b66f2d16SKris Kennaway ret = write(fd, data, len); 786043840dfSDag-Erling Smørgrav if (ret < 0) { 787b66f2d16SKris Kennaway error("process_write: write failed"); 788b66f2d16SKris Kennaway status = errno_to_portable(errno); 789043840dfSDag-Erling Smørgrav } else if ((size_t)ret == len) { 7901e8db6e2SBrian Feldman status = SSH2_FX_OK; 791761efaa7SDag-Erling Smørgrav handle_update_write(handle, ret); 792b66f2d16SKris Kennaway } else { 793761efaa7SDag-Erling Smørgrav debug2("nothing at all written"); 794b15c8340SDag-Erling Smørgrav status = SSH2_FX_FAILURE; 795b66f2d16SKris Kennaway } 796b66f2d16SKris Kennaway } 797b66f2d16SKris Kennaway } 798b66f2d16SKris Kennaway send_status(id, status); 799e4a9863fSDag-Erling Smørgrav free(data); 800b66f2d16SKris Kennaway } 801b66f2d16SKris Kennaway 802ae1f160dSDag-Erling Smørgrav static void 803f7167e0eSDag-Erling Smørgrav process_do_stat(u_int32_t id, int do_lstat) 804b66f2d16SKris Kennaway { 8051e8db6e2SBrian Feldman Attrib a; 806b66f2d16SKris Kennaway struct stat st; 807b66f2d16SKris Kennaway char *name; 8081e8db6e2SBrian Feldman int ret, status = SSH2_FX_FAILURE; 809b66f2d16SKris Kennaway 810b66f2d16SKris Kennaway name = get_string(NULL); 811761efaa7SDag-Erling Smørgrav debug3("request %u: %sstat", id, do_lstat ? "l" : ""); 812761efaa7SDag-Erling Smørgrav verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); 813b66f2d16SKris Kennaway ret = do_lstat ? lstat(name, &st) : stat(name, &st); 814b66f2d16SKris Kennaway if (ret < 0) { 815b66f2d16SKris Kennaway status = errno_to_portable(errno); 816b66f2d16SKris Kennaway } else { 8171e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 8181e8db6e2SBrian Feldman send_attrib(id, &a); 8191e8db6e2SBrian Feldman status = SSH2_FX_OK; 820b66f2d16SKris Kennaway } 8211e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 822b66f2d16SKris Kennaway send_status(id, status); 823e4a9863fSDag-Erling Smørgrav free(name); 824b66f2d16SKris Kennaway } 825b66f2d16SKris Kennaway 826ae1f160dSDag-Erling Smørgrav static void 827f7167e0eSDag-Erling Smørgrav process_stat(u_int32_t id) 828b66f2d16SKris Kennaway { 829f7167e0eSDag-Erling Smørgrav process_do_stat(id, 0); 830b66f2d16SKris Kennaway } 831b66f2d16SKris Kennaway 832ae1f160dSDag-Erling Smørgrav static void 833f7167e0eSDag-Erling Smørgrav process_lstat(u_int32_t id) 834b66f2d16SKris Kennaway { 835f7167e0eSDag-Erling Smørgrav process_do_stat(id, 1); 836b66f2d16SKris Kennaway } 837b66f2d16SKris Kennaway 838ae1f160dSDag-Erling Smørgrav static void 839f7167e0eSDag-Erling Smørgrav process_fstat(u_int32_t id) 840b66f2d16SKris Kennaway { 8411e8db6e2SBrian Feldman Attrib a; 842b66f2d16SKris Kennaway struct stat st; 8431e8db6e2SBrian Feldman int fd, ret, handle, status = SSH2_FX_FAILURE; 844b66f2d16SKris Kennaway 845b66f2d16SKris Kennaway handle = get_handle(); 846761efaa7SDag-Erling Smørgrav debug("request %u: fstat \"%s\" (handle %u)", 847761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle); 848b66f2d16SKris Kennaway fd = handle_to_fd(handle); 849b66f2d16SKris Kennaway if (fd >= 0) { 850b66f2d16SKris Kennaway ret = fstat(fd, &st); 851b66f2d16SKris Kennaway if (ret < 0) { 852b66f2d16SKris Kennaway status = errno_to_portable(errno); 853b66f2d16SKris Kennaway } else { 8541e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 8551e8db6e2SBrian Feldman send_attrib(id, &a); 8561e8db6e2SBrian Feldman status = SSH2_FX_OK; 857b66f2d16SKris Kennaway } 858b66f2d16SKris Kennaway } 8591e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 860b66f2d16SKris Kennaway send_status(id, status); 861b66f2d16SKris Kennaway } 862b66f2d16SKris Kennaway 863ae1f160dSDag-Erling Smørgrav static struct timeval * 864efcad6b7SDag-Erling Smørgrav attrib_to_tv(const Attrib *a) 865b66f2d16SKris Kennaway { 866b66f2d16SKris Kennaway static struct timeval tv[2]; 8671e8db6e2SBrian Feldman 868b66f2d16SKris Kennaway tv[0].tv_sec = a->atime; 869b66f2d16SKris Kennaway tv[0].tv_usec = 0; 870b66f2d16SKris Kennaway tv[1].tv_sec = a->mtime; 871b66f2d16SKris Kennaway tv[1].tv_usec = 0; 872b66f2d16SKris Kennaway return tv; 873b66f2d16SKris Kennaway } 874b66f2d16SKris Kennaway 875ae1f160dSDag-Erling Smørgrav static void 876f7167e0eSDag-Erling Smørgrav process_setstat(u_int32_t id) 877b66f2d16SKris Kennaway { 878b66f2d16SKris Kennaway Attrib *a; 879b66f2d16SKris Kennaway char *name; 880ee21a45fSDag-Erling Smørgrav int status = SSH2_FX_OK, ret; 881b66f2d16SKris Kennaway 882b66f2d16SKris Kennaway name = get_string(NULL); 883b66f2d16SKris Kennaway a = get_attrib(); 884761efaa7SDag-Erling Smørgrav debug("request %u: setstat name \"%s\"", id, name); 885ae1f160dSDag-Erling Smørgrav if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 886d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu", 887d4af9e69SDag-Erling Smørgrav name, (unsigned long long)a->size); 888ae1f160dSDag-Erling Smørgrav ret = truncate(name, a->size); 889ae1f160dSDag-Erling Smørgrav if (ret == -1) 890ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 891ae1f160dSDag-Erling Smørgrav } 8921e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 893761efaa7SDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a->perm); 894d4af9e69SDag-Erling Smørgrav ret = chmod(name, a->perm & 07777); 895b66f2d16SKris Kennaway if (ret == -1) 896b66f2d16SKris Kennaway status = errno_to_portable(errno); 897b66f2d16SKris Kennaway } 8981e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 899761efaa7SDag-Erling Smørgrav char buf[64]; 900761efaa7SDag-Erling Smørgrav time_t t = a->mtime; 901761efaa7SDag-Erling Smørgrav 902761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 903761efaa7SDag-Erling Smørgrav localtime(&t)); 904761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf); 905b66f2d16SKris Kennaway ret = utimes(name, attrib_to_tv(a)); 906b66f2d16SKris Kennaway if (ret == -1) 907b66f2d16SKris Kennaway status = errno_to_portable(errno); 908b66f2d16SKris Kennaway } 9091e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 910761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name, 911761efaa7SDag-Erling Smørgrav (u_long)a->uid, (u_long)a->gid); 9121e8db6e2SBrian Feldman ret = chown(name, a->uid, a->gid); 9131e8db6e2SBrian Feldman if (ret == -1) 9141e8db6e2SBrian Feldman status = errno_to_portable(errno); 9151e8db6e2SBrian Feldman } 916b66f2d16SKris Kennaway send_status(id, status); 917e4a9863fSDag-Erling Smørgrav free(name); 918b66f2d16SKris Kennaway } 919b66f2d16SKris Kennaway 920ae1f160dSDag-Erling Smørgrav static void 921f7167e0eSDag-Erling Smørgrav process_fsetstat(u_int32_t id) 922b66f2d16SKris Kennaway { 923b66f2d16SKris Kennaway Attrib *a; 924b66f2d16SKris Kennaway int handle, fd, ret; 9251e8db6e2SBrian Feldman int status = SSH2_FX_OK; 926b66f2d16SKris Kennaway 927b66f2d16SKris Kennaway handle = get_handle(); 928b66f2d16SKris Kennaway a = get_attrib(); 929761efaa7SDag-Erling Smørgrav debug("request %u: fsetstat handle %d", id, handle); 930b66f2d16SKris Kennaway fd = handle_to_fd(handle); 931b15c8340SDag-Erling Smørgrav if (fd < 0) 9321e8db6e2SBrian Feldman status = SSH2_FX_FAILURE; 933b15c8340SDag-Erling Smørgrav else { 934761efaa7SDag-Erling Smørgrav char *name = handle_to_name(handle); 935761efaa7SDag-Erling Smørgrav 936ae1f160dSDag-Erling Smørgrav if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 937d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu", 938d4af9e69SDag-Erling Smørgrav name, (unsigned long long)a->size); 939ae1f160dSDag-Erling Smørgrav ret = ftruncate(fd, a->size); 940ae1f160dSDag-Erling Smørgrav if (ret == -1) 941ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 942ae1f160dSDag-Erling Smørgrav } 9431e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 944761efaa7SDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a->perm); 94583d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 946d4af9e69SDag-Erling Smørgrav ret = fchmod(fd, a->perm & 07777); 94783d2307dSDag-Erling Smørgrav #else 948d4af9e69SDag-Erling Smørgrav ret = chmod(name, a->perm & 07777); 94983d2307dSDag-Erling Smørgrav #endif 950b66f2d16SKris Kennaway if (ret == -1) 951b66f2d16SKris Kennaway status = errno_to_portable(errno); 952b66f2d16SKris Kennaway } 9531e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 954761efaa7SDag-Erling Smørgrav char buf[64]; 955761efaa7SDag-Erling Smørgrav time_t t = a->mtime; 956761efaa7SDag-Erling Smørgrav 957761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 958761efaa7SDag-Erling Smørgrav localtime(&t)); 959761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf); 96083d2307dSDag-Erling Smørgrav #ifdef HAVE_FUTIMES 961b66f2d16SKris Kennaway ret = futimes(fd, attrib_to_tv(a)); 96283d2307dSDag-Erling Smørgrav #else 96383d2307dSDag-Erling Smørgrav ret = utimes(name, attrib_to_tv(a)); 96483d2307dSDag-Erling Smørgrav #endif 965b66f2d16SKris Kennaway if (ret == -1) 966b66f2d16SKris Kennaway status = errno_to_portable(errno); 967b66f2d16SKris Kennaway } 9681e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 969761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name, 970761efaa7SDag-Erling Smørgrav (u_long)a->uid, (u_long)a->gid); 97183d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHOWN 9721e8db6e2SBrian Feldman ret = fchown(fd, a->uid, a->gid); 97383d2307dSDag-Erling Smørgrav #else 97483d2307dSDag-Erling Smørgrav ret = chown(name, a->uid, a->gid); 97583d2307dSDag-Erling Smørgrav #endif 9761e8db6e2SBrian Feldman if (ret == -1) 9771e8db6e2SBrian Feldman status = errno_to_portable(errno); 9781e8db6e2SBrian Feldman } 979b66f2d16SKris Kennaway } 980b66f2d16SKris Kennaway send_status(id, status); 981b66f2d16SKris Kennaway } 982b66f2d16SKris Kennaway 983ae1f160dSDag-Erling Smørgrav static void 984f7167e0eSDag-Erling Smørgrav process_opendir(u_int32_t id) 985b66f2d16SKris Kennaway { 986b66f2d16SKris Kennaway DIR *dirp = NULL; 987b66f2d16SKris Kennaway char *path; 9881e8db6e2SBrian Feldman int handle, status = SSH2_FX_FAILURE; 989b66f2d16SKris Kennaway 990b66f2d16SKris Kennaway path = get_string(NULL); 991761efaa7SDag-Erling Smørgrav debug3("request %u: opendir", id); 992761efaa7SDag-Erling Smørgrav logit("opendir \"%s\"", path); 993b66f2d16SKris Kennaway dirp = opendir(path); 994b66f2d16SKris Kennaway if (dirp == NULL) { 995b66f2d16SKris Kennaway status = errno_to_portable(errno); 996b66f2d16SKris Kennaway } else { 997f7167e0eSDag-Erling Smørgrav handle = handle_new(HANDLE_DIR, path, 0, 0, dirp); 998b66f2d16SKris Kennaway if (handle < 0) { 999b66f2d16SKris Kennaway closedir(dirp); 1000b66f2d16SKris Kennaway } else { 1001b66f2d16SKris Kennaway send_handle(id, handle); 10021e8db6e2SBrian Feldman status = SSH2_FX_OK; 1003b66f2d16SKris Kennaway } 1004b66f2d16SKris Kennaway 1005b66f2d16SKris Kennaway } 10061e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 1007b66f2d16SKris Kennaway send_status(id, status); 1008e4a9863fSDag-Erling Smørgrav free(path); 1009b66f2d16SKris Kennaway } 1010b66f2d16SKris Kennaway 1011ae1f160dSDag-Erling Smørgrav static void 1012f7167e0eSDag-Erling Smørgrav process_readdir(u_int32_t id) 1013b66f2d16SKris Kennaway { 1014b66f2d16SKris Kennaway DIR *dirp; 1015b66f2d16SKris Kennaway struct dirent *dp; 1016b66f2d16SKris Kennaway char *path; 1017b66f2d16SKris Kennaway int handle; 1018b66f2d16SKris Kennaway 1019b66f2d16SKris Kennaway handle = get_handle(); 1020761efaa7SDag-Erling Smørgrav debug("request %u: readdir \"%s\" (handle %d)", id, 1021761efaa7SDag-Erling Smørgrav handle_to_name(handle), handle); 1022b66f2d16SKris Kennaway dirp = handle_to_dir(handle); 1023b66f2d16SKris Kennaway path = handle_to_name(handle); 1024b66f2d16SKris Kennaway if (dirp == NULL || path == NULL) { 10251e8db6e2SBrian Feldman send_status(id, SSH2_FX_FAILURE); 1026b66f2d16SKris Kennaway } else { 1027b66f2d16SKris Kennaway struct stat st; 1028761efaa7SDag-Erling Smørgrav char pathname[MAXPATHLEN]; 1029b66f2d16SKris Kennaway Stat *stats; 1030b66f2d16SKris Kennaway int nstats = 10, count = 0, i; 1031ee21a45fSDag-Erling Smørgrav 1032761efaa7SDag-Erling Smørgrav stats = xcalloc(nstats, sizeof(Stat)); 1033b66f2d16SKris Kennaway while ((dp = readdir(dirp)) != NULL) { 1034b66f2d16SKris Kennaway if (count >= nstats) { 1035b66f2d16SKris Kennaway nstats *= 2; 1036761efaa7SDag-Erling Smørgrav stats = xrealloc(stats, nstats, sizeof(Stat)); 1037b66f2d16SKris Kennaway } 1038b66f2d16SKris Kennaway /* XXX OVERFLOW ? */ 1039ae1f160dSDag-Erling Smørgrav snprintf(pathname, sizeof pathname, "%s%s%s", path, 1040ae1f160dSDag-Erling Smørgrav strcmp(path, "/") ? "/" : "", dp->d_name); 1041b66f2d16SKris Kennaway if (lstat(pathname, &st) < 0) 1042b66f2d16SKris Kennaway continue; 10431e8db6e2SBrian Feldman stat_to_attrib(&st, &(stats[count].attrib)); 1044b66f2d16SKris Kennaway stats[count].name = xstrdup(dp->d_name); 1045b15c8340SDag-Erling Smørgrav stats[count].long_name = ls_file(dp->d_name, &st, 0, 0); 1046b66f2d16SKris Kennaway count++; 1047b66f2d16SKris Kennaway /* send up to 100 entries in one message */ 10481e8db6e2SBrian Feldman /* XXX check packet size instead */ 1049b66f2d16SKris Kennaway if (count == 100) 1050b66f2d16SKris Kennaway break; 1051b66f2d16SKris Kennaway } 10521e8db6e2SBrian Feldman if (count > 0) { 1053b66f2d16SKris Kennaway send_names(id, count, stats); 1054b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 1055e4a9863fSDag-Erling Smørgrav free(stats[i].name); 1056e4a9863fSDag-Erling Smørgrav free(stats[i].long_name); 1057b66f2d16SKris Kennaway } 10581e8db6e2SBrian Feldman } else { 10591e8db6e2SBrian Feldman send_status(id, SSH2_FX_EOF); 10601e8db6e2SBrian Feldman } 1061e4a9863fSDag-Erling Smørgrav free(stats); 1062b66f2d16SKris Kennaway } 1063b66f2d16SKris Kennaway } 1064b66f2d16SKris Kennaway 1065ae1f160dSDag-Erling Smørgrav static void 1066f7167e0eSDag-Erling Smørgrav process_remove(u_int32_t id) 1067b66f2d16SKris Kennaway { 1068b66f2d16SKris Kennaway char *name; 10691e8db6e2SBrian Feldman int status = SSH2_FX_FAILURE; 1070b66f2d16SKris Kennaway int ret; 1071b66f2d16SKris Kennaway 1072b66f2d16SKris Kennaway name = get_string(NULL); 1073761efaa7SDag-Erling Smørgrav debug3("request %u: remove", id); 1074761efaa7SDag-Erling Smørgrav logit("remove name \"%s\"", name); 10751e8db6e2SBrian Feldman ret = unlink(name); 10761e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1077b66f2d16SKris Kennaway send_status(id, status); 1078e4a9863fSDag-Erling Smørgrav free(name); 1079b66f2d16SKris Kennaway } 1080b66f2d16SKris Kennaway 1081ae1f160dSDag-Erling Smørgrav static void 1082f7167e0eSDag-Erling Smørgrav process_mkdir(u_int32_t id) 1083b66f2d16SKris Kennaway { 1084b66f2d16SKris Kennaway Attrib *a; 1085b66f2d16SKris Kennaway char *name; 10861e8db6e2SBrian Feldman int ret, mode, status = SSH2_FX_FAILURE; 1087b66f2d16SKris Kennaway 1088b66f2d16SKris Kennaway name = get_string(NULL); 1089b66f2d16SKris Kennaway a = get_attrib(); 10901e8db6e2SBrian Feldman mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 1091d4af9e69SDag-Erling Smørgrav a->perm & 07777 : 0777; 1092761efaa7SDag-Erling Smørgrav debug3("request %u: mkdir", id); 1093761efaa7SDag-Erling Smørgrav logit("mkdir name \"%s\" mode 0%o", name, mode); 1094b66f2d16SKris Kennaway ret = mkdir(name, mode); 10951e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1096b66f2d16SKris Kennaway send_status(id, status); 1097e4a9863fSDag-Erling Smørgrav free(name); 1098b66f2d16SKris Kennaway } 1099b66f2d16SKris Kennaway 1100ae1f160dSDag-Erling Smørgrav static void 1101f7167e0eSDag-Erling Smørgrav process_rmdir(u_int32_t id) 1102b66f2d16SKris Kennaway { 1103b66f2d16SKris Kennaway char *name; 1104b66f2d16SKris Kennaway int ret, status; 1105b66f2d16SKris Kennaway 1106b66f2d16SKris Kennaway name = get_string(NULL); 1107761efaa7SDag-Erling Smørgrav debug3("request %u: rmdir", id); 1108761efaa7SDag-Erling Smørgrav logit("rmdir name \"%s\"", name); 1109b66f2d16SKris Kennaway ret = rmdir(name); 11101e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1111b66f2d16SKris Kennaway send_status(id, status); 1112e4a9863fSDag-Erling Smørgrav free(name); 1113b66f2d16SKris Kennaway } 1114b66f2d16SKris Kennaway 1115ae1f160dSDag-Erling Smørgrav static void 1116f7167e0eSDag-Erling Smørgrav process_realpath(u_int32_t id) 1117b66f2d16SKris Kennaway { 1118b66f2d16SKris Kennaway char resolvedname[MAXPATHLEN]; 1119b66f2d16SKris Kennaway char *path; 1120b66f2d16SKris Kennaway 1121b66f2d16SKris Kennaway path = get_string(NULL); 11221e8db6e2SBrian Feldman if (path[0] == '\0') { 1123e4a9863fSDag-Erling Smørgrav free(path); 11241e8db6e2SBrian Feldman path = xstrdup("."); 11251e8db6e2SBrian Feldman } 1126761efaa7SDag-Erling Smørgrav debug3("request %u: realpath", id); 1127761efaa7SDag-Erling Smørgrav verbose("realpath \"%s\"", path); 1128b66f2d16SKris Kennaway if (realpath(path, resolvedname) == NULL) { 1129b66f2d16SKris Kennaway send_status(id, errno_to_portable(errno)); 1130b66f2d16SKris Kennaway } else { 1131b66f2d16SKris Kennaway Stat s; 1132b66f2d16SKris Kennaway attrib_clear(&s.attrib); 1133b66f2d16SKris Kennaway s.name = s.long_name = resolvedname; 1134b66f2d16SKris Kennaway send_names(id, 1, &s); 1135b66f2d16SKris Kennaway } 1136e4a9863fSDag-Erling Smørgrav free(path); 1137b66f2d16SKris Kennaway } 1138b66f2d16SKris Kennaway 1139ae1f160dSDag-Erling Smørgrav static void 1140f7167e0eSDag-Erling Smørgrav process_rename(u_int32_t id) 1141b66f2d16SKris Kennaway { 1142b66f2d16SKris Kennaway char *oldpath, *newpath; 1143d0c8c0bcSDag-Erling Smørgrav int status; 1144d0c8c0bcSDag-Erling Smørgrav struct stat sb; 1145b66f2d16SKris Kennaway 1146b66f2d16SKris Kennaway oldpath = get_string(NULL); 1147b66f2d16SKris Kennaway newpath = get_string(NULL); 1148761efaa7SDag-Erling Smørgrav debug3("request %u: rename", id); 1149761efaa7SDag-Erling Smørgrav logit("rename old \"%s\" new \"%s\"", oldpath, newpath); 1150d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_FAILURE; 1151f7167e0eSDag-Erling Smørgrav if (lstat(oldpath, &sb) == -1) 1152d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1153d0c8c0bcSDag-Erling Smørgrav else if (S_ISREG(sb.st_mode)) { 1154d0c8c0bcSDag-Erling Smørgrav /* Race-free rename of regular files */ 1155d74d50a8SDag-Erling Smørgrav if (link(oldpath, newpath) == -1) { 11567aee6ffeSDag-Erling Smørgrav if (errno == EOPNOTSUPP || errno == ENOSYS 1157d4af9e69SDag-Erling Smørgrav #ifdef EXDEV 1158d4af9e69SDag-Erling Smørgrav || errno == EXDEV 1159d4af9e69SDag-Erling Smørgrav #endif 1160d74d50a8SDag-Erling Smørgrav #ifdef LINK_OPNOTSUPP_ERRNO 1161d74d50a8SDag-Erling Smørgrav || errno == LINK_OPNOTSUPP_ERRNO 1162d74d50a8SDag-Erling Smørgrav #endif 1163d74d50a8SDag-Erling Smørgrav ) { 1164d74d50a8SDag-Erling Smørgrav struct stat st; 1165d74d50a8SDag-Erling Smørgrav 1166d74d50a8SDag-Erling Smørgrav /* 1167d74d50a8SDag-Erling Smørgrav * fs doesn't support links, so fall back to 1168d74d50a8SDag-Erling Smørgrav * stat+rename. This is racy. 1169d74d50a8SDag-Erling Smørgrav */ 1170d74d50a8SDag-Erling Smørgrav if (stat(newpath, &st) == -1) { 1171d74d50a8SDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1172d74d50a8SDag-Erling Smørgrav status = 1173d74d50a8SDag-Erling Smørgrav errno_to_portable(errno); 1174d74d50a8SDag-Erling Smørgrav else 1175d74d50a8SDag-Erling Smørgrav status = SSH2_FX_OK; 1176d74d50a8SDag-Erling Smørgrav } 1177d74d50a8SDag-Erling Smørgrav } else { 1178d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1179d74d50a8SDag-Erling Smørgrav } 1180d74d50a8SDag-Erling Smørgrav } else if (unlink(oldpath) == -1) { 1181d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1182d0c8c0bcSDag-Erling Smørgrav /* clean spare link */ 1183d0c8c0bcSDag-Erling Smørgrav unlink(newpath); 1184d0c8c0bcSDag-Erling Smørgrav } else 1185d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 1186d0c8c0bcSDag-Erling Smørgrav } else if (stat(newpath, &sb) == -1) { 1187d0c8c0bcSDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1188d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1189d0c8c0bcSDag-Erling Smørgrav else 1190d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 11911e8db6e2SBrian Feldman } 1192b66f2d16SKris Kennaway send_status(id, status); 1193e4a9863fSDag-Erling Smørgrav free(oldpath); 1194e4a9863fSDag-Erling Smørgrav free(newpath); 1195b66f2d16SKris Kennaway } 1196b66f2d16SKris Kennaway 1197ae1f160dSDag-Erling Smørgrav static void 1198f7167e0eSDag-Erling Smørgrav process_readlink(u_int32_t id) 11991e8db6e2SBrian Feldman { 1200ae1f160dSDag-Erling Smørgrav int len; 1201d74d50a8SDag-Erling Smørgrav char buf[MAXPATHLEN]; 12021e8db6e2SBrian Feldman char *path; 12031e8db6e2SBrian Feldman 12041e8db6e2SBrian Feldman path = get_string(NULL); 1205761efaa7SDag-Erling Smørgrav debug3("request %u: readlink", id); 1206761efaa7SDag-Erling Smørgrav verbose("readlink \"%s\"", path); 1207d74d50a8SDag-Erling Smørgrav if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) 12081e8db6e2SBrian Feldman send_status(id, errno_to_portable(errno)); 12091e8db6e2SBrian Feldman else { 12101e8db6e2SBrian Feldman Stat s; 12111e8db6e2SBrian Feldman 1212d74d50a8SDag-Erling Smørgrav buf[len] = '\0'; 12131e8db6e2SBrian Feldman attrib_clear(&s.attrib); 1214d74d50a8SDag-Erling Smørgrav s.name = s.long_name = buf; 12151e8db6e2SBrian Feldman send_names(id, 1, &s); 12161e8db6e2SBrian Feldman } 1217e4a9863fSDag-Erling Smørgrav free(path); 12181e8db6e2SBrian Feldman } 12191e8db6e2SBrian Feldman 1220ae1f160dSDag-Erling Smørgrav static void 1221f7167e0eSDag-Erling Smørgrav process_symlink(u_int32_t id) 12221e8db6e2SBrian Feldman { 12231e8db6e2SBrian Feldman char *oldpath, *newpath; 1224d0c8c0bcSDag-Erling Smørgrav int ret, status; 12251e8db6e2SBrian Feldman 12261e8db6e2SBrian Feldman oldpath = get_string(NULL); 12271e8db6e2SBrian Feldman newpath = get_string(NULL); 1228761efaa7SDag-Erling Smørgrav debug3("request %u: symlink", id); 1229761efaa7SDag-Erling Smørgrav logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); 1230d0c8c0bcSDag-Erling Smørgrav /* this will fail if 'newpath' exists */ 12311e8db6e2SBrian Feldman ret = symlink(oldpath, newpath); 12321e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 12331e8db6e2SBrian Feldman send_status(id, status); 1234e4a9863fSDag-Erling Smørgrav free(oldpath); 1235e4a9863fSDag-Erling Smørgrav free(newpath); 12361e8db6e2SBrian Feldman } 12371e8db6e2SBrian Feldman 1238ae1f160dSDag-Erling Smørgrav static void 1239d4af9e69SDag-Erling Smørgrav process_extended_posix_rename(u_int32_t id) 1240d4af9e69SDag-Erling Smørgrav { 1241d4af9e69SDag-Erling Smørgrav char *oldpath, *newpath; 1242b15c8340SDag-Erling Smørgrav int ret, status; 1243d4af9e69SDag-Erling Smørgrav 1244d4af9e69SDag-Erling Smørgrav oldpath = get_string(NULL); 1245d4af9e69SDag-Erling Smørgrav newpath = get_string(NULL); 1246d4af9e69SDag-Erling Smørgrav debug3("request %u: posix-rename", id); 1247d4af9e69SDag-Erling Smørgrav logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); 1248b15c8340SDag-Erling Smørgrav ret = rename(oldpath, newpath); 1249b15c8340SDag-Erling Smørgrav status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1250b15c8340SDag-Erling Smørgrav send_status(id, status); 1251e4a9863fSDag-Erling Smørgrav free(oldpath); 1252e4a9863fSDag-Erling Smørgrav free(newpath); 1253d4af9e69SDag-Erling Smørgrav } 1254d4af9e69SDag-Erling Smørgrav 1255d4af9e69SDag-Erling Smørgrav static void 1256d4af9e69SDag-Erling Smørgrav process_extended_statvfs(u_int32_t id) 1257d4af9e69SDag-Erling Smørgrav { 1258d4af9e69SDag-Erling Smørgrav char *path; 1259d4af9e69SDag-Erling Smørgrav struct statvfs st; 1260d4af9e69SDag-Erling Smørgrav 1261d4af9e69SDag-Erling Smørgrav path = get_string(NULL); 1262f7167e0eSDag-Erling Smørgrav debug3("request %u: statvfs", id); 1263f7167e0eSDag-Erling Smørgrav logit("statvfs \"%s\"", path); 1264d4af9e69SDag-Erling Smørgrav 1265d4af9e69SDag-Erling Smørgrav if (statvfs(path, &st) != 0) 1266d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1267d4af9e69SDag-Erling Smørgrav else 1268d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st); 1269e4a9863fSDag-Erling Smørgrav free(path); 1270d4af9e69SDag-Erling Smørgrav } 1271d4af9e69SDag-Erling Smørgrav 1272d4af9e69SDag-Erling Smørgrav static void 1273d4af9e69SDag-Erling Smørgrav process_extended_fstatvfs(u_int32_t id) 1274d4af9e69SDag-Erling Smørgrav { 1275d4af9e69SDag-Erling Smørgrav int handle, fd; 1276d4af9e69SDag-Erling Smørgrav struct statvfs st; 1277d4af9e69SDag-Erling Smørgrav 1278d4af9e69SDag-Erling Smørgrav handle = get_handle(); 1279d4af9e69SDag-Erling Smørgrav debug("request %u: fstatvfs \"%s\" (handle %u)", 1280d4af9e69SDag-Erling Smørgrav id, handle_to_name(handle), handle); 1281d4af9e69SDag-Erling Smørgrav if ((fd = handle_to_fd(handle)) < 0) { 1282d4af9e69SDag-Erling Smørgrav send_status(id, SSH2_FX_FAILURE); 1283d4af9e69SDag-Erling Smørgrav return; 1284d4af9e69SDag-Erling Smørgrav } 1285d4af9e69SDag-Erling Smørgrav if (fstatvfs(fd, &st) != 0) 1286d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1287d4af9e69SDag-Erling Smørgrav else 1288d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st); 1289d4af9e69SDag-Erling Smørgrav } 1290d4af9e69SDag-Erling Smørgrav 1291d4af9e69SDag-Erling Smørgrav static void 12924a421b63SDag-Erling Smørgrav process_extended_hardlink(u_int32_t id) 12934a421b63SDag-Erling Smørgrav { 12944a421b63SDag-Erling Smørgrav char *oldpath, *newpath; 12954a421b63SDag-Erling Smørgrav int ret, status; 12964a421b63SDag-Erling Smørgrav 12974a421b63SDag-Erling Smørgrav oldpath = get_string(NULL); 12984a421b63SDag-Erling Smørgrav newpath = get_string(NULL); 12994a421b63SDag-Erling Smørgrav debug3("request %u: hardlink", id); 13004a421b63SDag-Erling Smørgrav logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath); 13014a421b63SDag-Erling Smørgrav ret = link(oldpath, newpath); 13024a421b63SDag-Erling Smørgrav status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 13034a421b63SDag-Erling Smørgrav send_status(id, status); 1304e4a9863fSDag-Erling Smørgrav free(oldpath); 1305e4a9863fSDag-Erling Smørgrav free(newpath); 13064a421b63SDag-Erling Smørgrav } 13074a421b63SDag-Erling Smørgrav 13084a421b63SDag-Erling Smørgrav static void 1309f7167e0eSDag-Erling Smørgrav process_extended_fsync(u_int32_t id) 13101e8db6e2SBrian Feldman { 1311f7167e0eSDag-Erling Smørgrav int handle, fd, ret, status = SSH2_FX_OP_UNSUPPORTED; 13121e8db6e2SBrian Feldman 1313f7167e0eSDag-Erling Smørgrav handle = get_handle(); 1314f7167e0eSDag-Erling Smørgrav debug3("request %u: fsync (handle %u)", id, handle); 1315f7167e0eSDag-Erling Smørgrav verbose("fsync \"%s\"", handle_to_name(handle)); 1316f7167e0eSDag-Erling Smørgrav if ((fd = handle_to_fd(handle)) < 0) 1317f7167e0eSDag-Erling Smørgrav status = SSH2_FX_NO_SUCH_FILE; 1318f7167e0eSDag-Erling Smørgrav else if (handle_is_ok(handle, HANDLE_FILE)) { 1319f7167e0eSDag-Erling Smørgrav ret = fsync(fd); 1320f7167e0eSDag-Erling Smørgrav status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1321f7167e0eSDag-Erling Smørgrav } 1322f7167e0eSDag-Erling Smørgrav send_status(id, status); 1323f7167e0eSDag-Erling Smørgrav } 1324f7167e0eSDag-Erling Smørgrav 1325f7167e0eSDag-Erling Smørgrav static void 1326f7167e0eSDag-Erling Smørgrav process_extended(u_int32_t id) 1327f7167e0eSDag-Erling Smørgrav { 1328f7167e0eSDag-Erling Smørgrav char *request; 1329f7167e0eSDag-Erling Smørgrav u_int i; 1330f7167e0eSDag-Erling Smørgrav 13311e8db6e2SBrian Feldman request = get_string(NULL); 1332f7167e0eSDag-Erling Smørgrav for (i = 0; extended_handlers[i].handler != NULL; i++) { 1333f7167e0eSDag-Erling Smørgrav if (strcmp(request, extended_handlers[i].ext_name) == 0) { 1334f7167e0eSDag-Erling Smørgrav if (!request_permitted(&extended_handlers[i])) 1335f7167e0eSDag-Erling Smørgrav send_status(id, SSH2_FX_PERMISSION_DENIED); 1336d4af9e69SDag-Erling Smørgrav else 1337f7167e0eSDag-Erling Smørgrav extended_handlers[i].handler(id); 1338f7167e0eSDag-Erling Smørgrav break; 1339f7167e0eSDag-Erling Smørgrav } 1340f7167e0eSDag-Erling Smørgrav } 1341f7167e0eSDag-Erling Smørgrav if (extended_handlers[i].handler == NULL) { 1342f7167e0eSDag-Erling Smørgrav error("Unknown extended request \"%.100s\"", request); 13431e8db6e2SBrian Feldman send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 1344f7167e0eSDag-Erling Smørgrav } 1345e4a9863fSDag-Erling Smørgrav free(request); 13461e8db6e2SBrian Feldman } 1347b66f2d16SKris Kennaway 1348b66f2d16SKris Kennaway /* stolen from ssh-agent */ 1349b66f2d16SKris Kennaway 1350ae1f160dSDag-Erling Smørgrav static void 1351b66f2d16SKris Kennaway process(void) 1352b66f2d16SKris Kennaway { 1353f7167e0eSDag-Erling Smørgrav u_int msg_len, buf_len, consumed, type, i; 13541e8db6e2SBrian Feldman u_char *cp; 1355f7167e0eSDag-Erling Smørgrav u_int32_t id; 1356b66f2d16SKris Kennaway 1357545d5ecaSDag-Erling Smørgrav buf_len = buffer_len(&iqueue); 1358545d5ecaSDag-Erling Smørgrav if (buf_len < 5) 1359b66f2d16SKris Kennaway return; /* Incomplete message. */ 1360ae1f160dSDag-Erling Smørgrav cp = buffer_ptr(&iqueue); 1361761efaa7SDag-Erling Smørgrav msg_len = get_u32(cp); 1362021d409fSDag-Erling Smørgrav if (msg_len > SFTP_MAX_MSG_LENGTH) { 1363761efaa7SDag-Erling Smørgrav error("bad message from %s local user %s", 1364761efaa7SDag-Erling Smørgrav client_addr, pw->pw_name); 1365d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(11); 1366b66f2d16SKris Kennaway } 1367545d5ecaSDag-Erling Smørgrav if (buf_len < msg_len + 4) 1368b66f2d16SKris Kennaway return; 1369b66f2d16SKris Kennaway buffer_consume(&iqueue, 4); 1370545d5ecaSDag-Erling Smørgrav buf_len -= 4; 1371b66f2d16SKris Kennaway type = buffer_get_char(&iqueue); 1372f7167e0eSDag-Erling Smørgrav 1373b66f2d16SKris Kennaway switch (type) { 13741e8db6e2SBrian Feldman case SSH2_FXP_INIT: 1375b66f2d16SKris Kennaway process_init(); 1376f7167e0eSDag-Erling Smørgrav init_done = 1; 13771e8db6e2SBrian Feldman break; 13781e8db6e2SBrian Feldman case SSH2_FXP_EXTENDED: 1379f7167e0eSDag-Erling Smørgrav if (!init_done) 1380f7167e0eSDag-Erling Smørgrav fatal("Received extended request before init"); 1381f7167e0eSDag-Erling Smørgrav id = get_int(); 1382f7167e0eSDag-Erling Smørgrav process_extended(id); 13831e8db6e2SBrian Feldman break; 1384b66f2d16SKris Kennaway default: 1385f7167e0eSDag-Erling Smørgrav if (!init_done) 1386f7167e0eSDag-Erling Smørgrav fatal("Received %u request before init", type); 1387f7167e0eSDag-Erling Smørgrav id = get_int(); 1388f7167e0eSDag-Erling Smørgrav for (i = 0; handlers[i].handler != NULL; i++) { 1389f7167e0eSDag-Erling Smørgrav if (type == handlers[i].type) { 1390f7167e0eSDag-Erling Smørgrav if (!request_permitted(&handlers[i])) { 1391f7167e0eSDag-Erling Smørgrav send_status(id, 1392f7167e0eSDag-Erling Smørgrav SSH2_FX_PERMISSION_DENIED); 1393f7167e0eSDag-Erling Smørgrav } else { 1394f7167e0eSDag-Erling Smørgrav handlers[i].handler(id); 1395f7167e0eSDag-Erling Smørgrav } 1396b66f2d16SKris Kennaway break; 1397b66f2d16SKris Kennaway } 1398f7167e0eSDag-Erling Smørgrav } 1399f7167e0eSDag-Erling Smørgrav if (handlers[i].handler == NULL) 1400f7167e0eSDag-Erling Smørgrav error("Unknown message %u", type); 1401f7167e0eSDag-Erling Smørgrav } 1402545d5ecaSDag-Erling Smørgrav /* discard the remaining bytes from the current packet */ 1403d4af9e69SDag-Erling Smørgrav if (buf_len < buffer_len(&iqueue)) { 1404d4af9e69SDag-Erling Smørgrav error("iqueue grew unexpectedly"); 1405d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1406d4af9e69SDag-Erling Smørgrav } 1407545d5ecaSDag-Erling Smørgrav consumed = buf_len - buffer_len(&iqueue); 1408d4af9e69SDag-Erling Smørgrav if (msg_len < consumed) { 1409f7167e0eSDag-Erling Smørgrav error("msg_len %u < consumed %u", msg_len, consumed); 1410d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1411d4af9e69SDag-Erling Smørgrav } 1412545d5ecaSDag-Erling Smørgrav if (msg_len > consumed) 1413545d5ecaSDag-Erling Smørgrav buffer_consume(&iqueue, msg_len - consumed); 1414b66f2d16SKris Kennaway } 1415b66f2d16SKris Kennaway 1416761efaa7SDag-Erling Smørgrav /* Cleanup handler that logs active handles upon normal exit */ 1417761efaa7SDag-Erling Smørgrav void 1418d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(int i) 1419761efaa7SDag-Erling Smørgrav { 1420761efaa7SDag-Erling Smørgrav if (pw != NULL && client_addr != NULL) { 1421761efaa7SDag-Erling Smørgrav handle_log_exit(); 1422761efaa7SDag-Erling Smørgrav logit("session closed for local user %s from [%s]", 1423761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr); 1424761efaa7SDag-Erling Smørgrav } 1425761efaa7SDag-Erling Smørgrav _exit(i); 1426761efaa7SDag-Erling Smørgrav } 1427761efaa7SDag-Erling Smørgrav 1428761efaa7SDag-Erling Smørgrav static void 1429d4af9e69SDag-Erling Smørgrav sftp_server_usage(void) 1430761efaa7SDag-Erling Smørgrav { 1431761efaa7SDag-Erling Smørgrav extern char *__progname; 1432761efaa7SDag-Erling Smørgrav 1433761efaa7SDag-Erling Smørgrav fprintf(stderr, 14346888a9beSDag-Erling Smørgrav "usage: %s [-ehR] [-d start_directory] [-f log_facility] " 1435f7167e0eSDag-Erling Smørgrav "[-l log_level]\n\t[-P blacklisted_requests] " 1436f7167e0eSDag-Erling Smørgrav "[-p whitelisted_requests] [-u umask]\n" 1437f7167e0eSDag-Erling Smørgrav " %s -Q protocol_feature\n", 1438f7167e0eSDag-Erling Smørgrav __progname, __progname); 1439761efaa7SDag-Erling Smørgrav exit(1); 1440761efaa7SDag-Erling Smørgrav } 1441761efaa7SDag-Erling Smørgrav 1442b66f2d16SKris Kennaway int 1443d4af9e69SDag-Erling Smørgrav sftp_server_main(int argc, char **argv, struct passwd *user_pw) 1444b66f2d16SKris Kennaway { 14451e8db6e2SBrian Feldman fd_set *rset, *wset; 1446f7167e0eSDag-Erling Smørgrav int i, in, out, max, ch, skipargs = 0, log_stderr = 0; 14471e8db6e2SBrian Feldman ssize_t len, olen, set_size; 1448761efaa7SDag-Erling Smørgrav SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; 14496888a9beSDag-Erling Smørgrav char *cp, *homedir = NULL, buf[4*4096]; 14504a421b63SDag-Erling Smørgrav long mask; 1451761efaa7SDag-Erling Smørgrav 1452761efaa7SDag-Erling Smørgrav extern char *optarg; 1453761efaa7SDag-Erling Smørgrav extern char *__progname; 14541e8db6e2SBrian Feldman 1455761efaa7SDag-Erling Smørgrav __progname = ssh_get_progname(argv[0]); 1456761efaa7SDag-Erling Smørgrav log_init(__progname, log_level, log_facility, log_stderr); 1457b66f2d16SKris Kennaway 14586888a9beSDag-Erling Smørgrav pw = pwcopy(user_pw); 14596888a9beSDag-Erling Smørgrav 1460f7167e0eSDag-Erling Smørgrav while (!skipargs && (ch = getopt(argc, argv, 1461f7167e0eSDag-Erling Smørgrav "d:f:l:P:p:Q:u:cehR")) != -1) { 1462761efaa7SDag-Erling Smørgrav switch (ch) { 1463f7167e0eSDag-Erling Smørgrav case 'Q': 1464f7167e0eSDag-Erling Smørgrav if (strcasecmp(optarg, "requests") != 0) { 1465f7167e0eSDag-Erling Smørgrav fprintf(stderr, "Invalid query type\n"); 1466f7167e0eSDag-Erling Smørgrav exit(1); 1467f7167e0eSDag-Erling Smørgrav } 1468f7167e0eSDag-Erling Smørgrav for (i = 0; handlers[i].handler != NULL; i++) 1469f7167e0eSDag-Erling Smørgrav printf("%s\n", handlers[i].name); 1470f7167e0eSDag-Erling Smørgrav for (i = 0; extended_handlers[i].handler != NULL; i++) 1471f7167e0eSDag-Erling Smørgrav printf("%s\n", extended_handlers[i].name); 1472f7167e0eSDag-Erling Smørgrav exit(0); 1473f7167e0eSDag-Erling Smørgrav break; 1474b15c8340SDag-Erling Smørgrav case 'R': 1475b15c8340SDag-Erling Smørgrav readonly = 1; 1476b15c8340SDag-Erling Smørgrav break; 1477761efaa7SDag-Erling Smørgrav case 'c': 1478761efaa7SDag-Erling Smørgrav /* 1479761efaa7SDag-Erling Smørgrav * Ignore all arguments if we are invoked as a 1480761efaa7SDag-Erling Smørgrav * shell using "sftp-server -c command" 1481761efaa7SDag-Erling Smørgrav */ 1482761efaa7SDag-Erling Smørgrav skipargs = 1; 1483761efaa7SDag-Erling Smørgrav break; 1484761efaa7SDag-Erling Smørgrav case 'e': 1485761efaa7SDag-Erling Smørgrav log_stderr = 1; 1486761efaa7SDag-Erling Smørgrav break; 1487761efaa7SDag-Erling Smørgrav case 'l': 1488761efaa7SDag-Erling Smørgrav log_level = log_level_number(optarg); 1489761efaa7SDag-Erling Smørgrav if (log_level == SYSLOG_LEVEL_NOT_SET) 1490761efaa7SDag-Erling Smørgrav error("Invalid log level \"%s\"", optarg); 1491761efaa7SDag-Erling Smørgrav break; 1492761efaa7SDag-Erling Smørgrav case 'f': 1493761efaa7SDag-Erling Smørgrav log_facility = log_facility_number(optarg); 1494d4af9e69SDag-Erling Smørgrav if (log_facility == SYSLOG_FACILITY_NOT_SET) 1495761efaa7SDag-Erling Smørgrav error("Invalid log facility \"%s\"", optarg); 1496761efaa7SDag-Erling Smørgrav break; 14976888a9beSDag-Erling Smørgrav case 'd': 14986888a9beSDag-Erling Smørgrav cp = tilde_expand_filename(optarg, user_pw->pw_uid); 14996888a9beSDag-Erling Smørgrav homedir = percent_expand(cp, "d", user_pw->pw_dir, 15006888a9beSDag-Erling Smørgrav "u", user_pw->pw_name, (char *)NULL); 15016888a9beSDag-Erling Smørgrav free(cp); 15026888a9beSDag-Erling Smørgrav break; 1503f7167e0eSDag-Erling Smørgrav case 'p': 1504f7167e0eSDag-Erling Smørgrav if (request_whitelist != NULL) 1505f7167e0eSDag-Erling Smørgrav fatal("Permitted requests already set"); 1506f7167e0eSDag-Erling Smørgrav request_whitelist = xstrdup(optarg); 1507f7167e0eSDag-Erling Smørgrav break; 1508f7167e0eSDag-Erling Smørgrav case 'P': 1509f7167e0eSDag-Erling Smørgrav if (request_blacklist != NULL) 1510f7167e0eSDag-Erling Smørgrav fatal("Refused requests already set"); 1511f7167e0eSDag-Erling Smørgrav request_blacklist = xstrdup(optarg); 1512f7167e0eSDag-Erling Smørgrav break; 1513b15c8340SDag-Erling Smørgrav case 'u': 15144a421b63SDag-Erling Smørgrav errno = 0; 15154a421b63SDag-Erling Smørgrav mask = strtol(optarg, &cp, 8); 15164a421b63SDag-Erling Smørgrav if (mask < 0 || mask > 0777 || *cp != '\0' || 15174a421b63SDag-Erling Smørgrav cp == optarg || (mask == 0 && errno != 0)) 15184a421b63SDag-Erling Smørgrav fatal("Invalid umask \"%s\"", optarg); 15194a421b63SDag-Erling Smørgrav (void)umask((mode_t)mask); 1520b15c8340SDag-Erling Smørgrav break; 1521761efaa7SDag-Erling Smørgrav case 'h': 1522761efaa7SDag-Erling Smørgrav default: 1523d4af9e69SDag-Erling Smørgrav sftp_server_usage(); 1524761efaa7SDag-Erling Smørgrav } 1525761efaa7SDag-Erling Smørgrav } 1526761efaa7SDag-Erling Smørgrav 1527761efaa7SDag-Erling Smørgrav log_init(__progname, log_level, log_facility, log_stderr); 1528761efaa7SDag-Erling Smørgrav 1529*a0ee8cc6SDag-Erling Smørgrav #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) 1530*a0ee8cc6SDag-Erling Smørgrav /* 1531*a0ee8cc6SDag-Erling Smørgrav * On Linux, we should try to avoid making /proc/self/{mem,maps} 1532*a0ee8cc6SDag-Erling Smørgrav * available to the user so that sftp access doesn't automatically 1533*a0ee8cc6SDag-Erling Smørgrav * imply arbitrary code execution access that will break 1534*a0ee8cc6SDag-Erling Smørgrav * restricted configurations. 1535*a0ee8cc6SDag-Erling Smørgrav */ 1536*a0ee8cc6SDag-Erling Smørgrav if (prctl(PR_SET_DUMPABLE, 0) != 0) 1537*a0ee8cc6SDag-Erling Smørgrav fatal("unable to make the process undumpable"); 1538*a0ee8cc6SDag-Erling Smørgrav #endif /* defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) */ 1539*a0ee8cc6SDag-Erling Smørgrav 1540761efaa7SDag-Erling Smørgrav if ((cp = getenv("SSH_CONNECTION")) != NULL) { 1541761efaa7SDag-Erling Smørgrav client_addr = xstrdup(cp); 1542d4af9e69SDag-Erling Smørgrav if ((cp = strchr(client_addr, ' ')) == NULL) { 1543d4af9e69SDag-Erling Smørgrav error("Malformed SSH_CONNECTION variable: \"%s\"", 1544761efaa7SDag-Erling Smørgrav getenv("SSH_CONNECTION")); 1545d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1546d4af9e69SDag-Erling Smørgrav } 1547761efaa7SDag-Erling Smørgrav *cp = '\0'; 1548761efaa7SDag-Erling Smørgrav } else 1549761efaa7SDag-Erling Smørgrav client_addr = xstrdup("UNKNOWN"); 1550761efaa7SDag-Erling Smørgrav 1551761efaa7SDag-Erling Smørgrav logit("session opened for local user %s from [%s]", 1552761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr); 1553761efaa7SDag-Erling Smørgrav 1554b15c8340SDag-Erling Smørgrav in = STDIN_FILENO; 1555b15c8340SDag-Erling Smørgrav out = STDOUT_FILENO; 1556b66f2d16SKris Kennaway 155783d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN 155883d2307dSDag-Erling Smørgrav setmode(in, O_BINARY); 155983d2307dSDag-Erling Smørgrav setmode(out, O_BINARY); 156083d2307dSDag-Erling Smørgrav #endif 156183d2307dSDag-Erling Smørgrav 1562b66f2d16SKris Kennaway max = 0; 1563b66f2d16SKris Kennaway if (in > max) 1564b66f2d16SKris Kennaway max = in; 1565b66f2d16SKris Kennaway if (out > max) 1566b66f2d16SKris Kennaway max = out; 1567b66f2d16SKris Kennaway 1568b66f2d16SKris Kennaway buffer_init(&iqueue); 1569b66f2d16SKris Kennaway buffer_init(&oqueue); 1570b66f2d16SKris Kennaway 15711e8db6e2SBrian Feldman set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 15721e8db6e2SBrian Feldman rset = (fd_set *)xmalloc(set_size); 15731e8db6e2SBrian Feldman wset = (fd_set *)xmalloc(set_size); 1574b66f2d16SKris Kennaway 15756888a9beSDag-Erling Smørgrav if (homedir != NULL) { 15766888a9beSDag-Erling Smørgrav if (chdir(homedir) != 0) { 15776888a9beSDag-Erling Smørgrav error("chdir to \"%s\" failed: %s", homedir, 15786888a9beSDag-Erling Smørgrav strerror(errno)); 15796888a9beSDag-Erling Smørgrav } 15806888a9beSDag-Erling Smørgrav } 15816888a9beSDag-Erling Smørgrav 15821e8db6e2SBrian Feldman for (;;) { 15831e8db6e2SBrian Feldman memset(rset, 0, set_size); 15841e8db6e2SBrian Feldman memset(wset, 0, set_size); 15851e8db6e2SBrian Feldman 1586d4af9e69SDag-Erling Smørgrav /* 1587d4af9e69SDag-Erling Smørgrav * Ensure that we can read a full buffer and handle 1588d4af9e69SDag-Erling Smørgrav * the worst-case length packet it can generate, 1589d4af9e69SDag-Erling Smørgrav * otherwise apply backpressure by stopping reads. 1590d4af9e69SDag-Erling Smørgrav */ 1591d4af9e69SDag-Erling Smørgrav if (buffer_check_alloc(&iqueue, sizeof(buf)) && 1592d4af9e69SDag-Erling Smørgrav buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) 15931e8db6e2SBrian Feldman FD_SET(in, rset); 1594d4af9e69SDag-Erling Smørgrav 1595b66f2d16SKris Kennaway olen = buffer_len(&oqueue); 1596b66f2d16SKris Kennaway if (olen > 0) 15971e8db6e2SBrian Feldman FD_SET(out, wset); 1598b66f2d16SKris Kennaway 15991e8db6e2SBrian Feldman if (select(max+1, rset, wset, NULL, NULL) < 0) { 1600b66f2d16SKris Kennaway if (errno == EINTR) 1601b66f2d16SKris Kennaway continue; 1602761efaa7SDag-Erling Smørgrav error("select: %s", strerror(errno)); 1603d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(2); 1604b66f2d16SKris Kennaway } 1605b66f2d16SKris Kennaway 1606b66f2d16SKris Kennaway /* copy stdin to iqueue */ 16071e8db6e2SBrian Feldman if (FD_ISSET(in, rset)) { 1608b66f2d16SKris Kennaway len = read(in, buf, sizeof buf); 1609b66f2d16SKris Kennaway if (len == 0) { 1610b66f2d16SKris Kennaway debug("read eof"); 1611d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(0); 1612b66f2d16SKris Kennaway } else if (len < 0) { 1613761efaa7SDag-Erling Smørgrav error("read: %s", strerror(errno)); 1614d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1); 1615b66f2d16SKris Kennaway } else { 1616b66f2d16SKris Kennaway buffer_append(&iqueue, buf, len); 1617b66f2d16SKris Kennaway } 1618b66f2d16SKris Kennaway } 1619b66f2d16SKris Kennaway /* send oqueue to stdout */ 16201e8db6e2SBrian Feldman if (FD_ISSET(out, wset)) { 1621b66f2d16SKris Kennaway len = write(out, buffer_ptr(&oqueue), olen); 1622b66f2d16SKris Kennaway if (len < 0) { 1623761efaa7SDag-Erling Smørgrav error("write: %s", strerror(errno)); 1624d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1); 1625b66f2d16SKris Kennaway } else { 1626b66f2d16SKris Kennaway buffer_consume(&oqueue, len); 1627b66f2d16SKris Kennaway } 1628b66f2d16SKris Kennaway } 1629d4af9e69SDag-Erling Smørgrav 1630d4af9e69SDag-Erling Smørgrav /* 1631d4af9e69SDag-Erling Smørgrav * Process requests from client if we can fit the results 1632d4af9e69SDag-Erling Smørgrav * into the output buffer, otherwise stop processing input 1633d4af9e69SDag-Erling Smørgrav * and let the output queue drain. 1634d4af9e69SDag-Erling Smørgrav */ 1635d4af9e69SDag-Erling Smørgrav if (buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) 1636b66f2d16SKris Kennaway process(); 1637b66f2d16SKris Kennaway } 1638b66f2d16SKris Kennaway } 1639