1*e4a9863fSDag-Erling Smørgrav /* $OpenBSD: sftp-server.c,v 1.97 2013/05/17 00:13:14 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/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 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> 36761efaa7SDag-Erling Smørgrav #include <pwd.h> 37761efaa7SDag-Erling Smørgrav #include <stdlib.h> 38761efaa7SDag-Erling Smørgrav #include <stdio.h> 39761efaa7SDag-Erling Smørgrav #include <string.h> 40761efaa7SDag-Erling Smørgrav #include <pwd.h> 41761efaa7SDag-Erling Smørgrav #include <time.h> 42761efaa7SDag-Erling Smørgrav #include <unistd.h> 43761efaa7SDag-Erling Smørgrav #include <stdarg.h> 44761efaa7SDag-Erling Smørgrav 45b66f2d16SKris Kennaway #include "xmalloc.h" 46761efaa7SDag-Erling Smørgrav #include "buffer.h" 47761efaa7SDag-Erling Smørgrav #include "log.h" 48021d409fSDag-Erling Smørgrav #include "misc.h" 49761efaa7SDag-Erling Smørgrav #include "uidswap.h" 50b66f2d16SKris Kennaway 511e8db6e2SBrian Feldman #include "sftp.h" 521e8db6e2SBrian Feldman #include "sftp-common.h" 53b66f2d16SKris Kennaway 54b66f2d16SKris Kennaway /* helper */ 551e8db6e2SBrian Feldman #define get_int64() buffer_get_int64(&iqueue); 56b66f2d16SKris Kennaway #define get_int() buffer_get_int(&iqueue); 57b66f2d16SKris Kennaway #define get_string(lenp) buffer_get_string(&iqueue, lenp); 58b66f2d16SKris Kennaway 59761efaa7SDag-Erling Smørgrav /* Our verbosity */ 60761efaa7SDag-Erling Smørgrav LogLevel log_level = SYSLOG_LEVEL_ERROR; 61761efaa7SDag-Erling Smørgrav 62761efaa7SDag-Erling Smørgrav /* Our client */ 63761efaa7SDag-Erling Smørgrav struct passwd *pw = NULL; 64761efaa7SDag-Erling Smørgrav char *client_addr = NULL; 6583d2307dSDag-Erling Smørgrav 66b66f2d16SKris Kennaway /* input and output queue */ 67b66f2d16SKris Kennaway Buffer iqueue; 68b66f2d16SKris Kennaway Buffer oqueue; 69b66f2d16SKris Kennaway 701e8db6e2SBrian Feldman /* Version of client */ 71e146993eSDag-Erling Smørgrav u_int version; 721e8db6e2SBrian Feldman 73b15c8340SDag-Erling Smørgrav /* Disable writes */ 74b15c8340SDag-Erling Smørgrav int readonly; 75b15c8340SDag-Erling Smørgrav 76d95e11bfSDag-Erling Smørgrav /* portable attributes, etc. */ 77b66f2d16SKris Kennaway 78b66f2d16SKris Kennaway typedef struct Stat Stat; 79b66f2d16SKris Kennaway 801e8db6e2SBrian Feldman struct Stat { 81b66f2d16SKris Kennaway char *name; 82b66f2d16SKris Kennaway char *long_name; 83b66f2d16SKris Kennaway Attrib attrib; 84b66f2d16SKris Kennaway }; 85b66f2d16SKris Kennaway 86ae1f160dSDag-Erling Smørgrav static int 87b66f2d16SKris Kennaway errno_to_portable(int unixerrno) 88b66f2d16SKris Kennaway { 89b66f2d16SKris Kennaway int ret = 0; 901e8db6e2SBrian Feldman 91b66f2d16SKris Kennaway switch (unixerrno) { 92b66f2d16SKris Kennaway case 0: 931e8db6e2SBrian Feldman ret = SSH2_FX_OK; 94b66f2d16SKris Kennaway break; 95b66f2d16SKris Kennaway case ENOENT: 96b66f2d16SKris Kennaway case ENOTDIR: 97b66f2d16SKris Kennaway case EBADF: 98b66f2d16SKris Kennaway case ELOOP: 991e8db6e2SBrian Feldman ret = SSH2_FX_NO_SUCH_FILE; 100b66f2d16SKris Kennaway break; 101b66f2d16SKris Kennaway case EPERM: 102b66f2d16SKris Kennaway case EACCES: 103b66f2d16SKris Kennaway case EFAULT: 1041e8db6e2SBrian Feldman ret = SSH2_FX_PERMISSION_DENIED; 105b66f2d16SKris Kennaway break; 106b66f2d16SKris Kennaway case ENAMETOOLONG: 107b66f2d16SKris Kennaway case EINVAL: 1081e8db6e2SBrian Feldman ret = SSH2_FX_BAD_MESSAGE; 109b66f2d16SKris Kennaway break; 110d4af9e69SDag-Erling Smørgrav case ENOSYS: 111d4af9e69SDag-Erling Smørgrav ret = SSH2_FX_OP_UNSUPPORTED; 112d4af9e69SDag-Erling Smørgrav break; 113b66f2d16SKris Kennaway default: 1141e8db6e2SBrian Feldman ret = SSH2_FX_FAILURE; 115b66f2d16SKris Kennaway break; 116b66f2d16SKris Kennaway } 117b66f2d16SKris Kennaway return ret; 118b66f2d16SKris Kennaway } 119b66f2d16SKris Kennaway 120ae1f160dSDag-Erling Smørgrav static int 121b66f2d16SKris Kennaway flags_from_portable(int pflags) 122b66f2d16SKris Kennaway { 123b66f2d16SKris Kennaway int flags = 0; 1241e8db6e2SBrian Feldman 1251e8db6e2SBrian Feldman if ((pflags & SSH2_FXF_READ) && 1261e8db6e2SBrian Feldman (pflags & SSH2_FXF_WRITE)) { 127b66f2d16SKris Kennaway flags = O_RDWR; 1281e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_READ) { 129b66f2d16SKris Kennaway flags = O_RDONLY; 1301e8db6e2SBrian Feldman } else if (pflags & SSH2_FXF_WRITE) { 131b66f2d16SKris Kennaway flags = O_WRONLY; 132b66f2d16SKris Kennaway } 1331e8db6e2SBrian Feldman if (pflags & SSH2_FXF_CREAT) 134b66f2d16SKris Kennaway flags |= O_CREAT; 1351e8db6e2SBrian Feldman if (pflags & SSH2_FXF_TRUNC) 136b66f2d16SKris Kennaway flags |= O_TRUNC; 1371e8db6e2SBrian Feldman if (pflags & SSH2_FXF_EXCL) 138b66f2d16SKris Kennaway flags |= O_EXCL; 139b66f2d16SKris Kennaway return flags; 140b66f2d16SKris Kennaway } 141b66f2d16SKris Kennaway 142761efaa7SDag-Erling Smørgrav static const char * 143761efaa7SDag-Erling Smørgrav string_from_portable(int pflags) 144761efaa7SDag-Erling Smørgrav { 145761efaa7SDag-Erling Smørgrav static char ret[128]; 146761efaa7SDag-Erling Smørgrav 147761efaa7SDag-Erling Smørgrav *ret = '\0'; 148761efaa7SDag-Erling Smørgrav 149761efaa7SDag-Erling Smørgrav #define PAPPEND(str) { \ 150761efaa7SDag-Erling Smørgrav if (*ret != '\0') \ 151761efaa7SDag-Erling Smørgrav strlcat(ret, ",", sizeof(ret)); \ 152761efaa7SDag-Erling Smørgrav strlcat(ret, str, sizeof(ret)); \ 153761efaa7SDag-Erling Smørgrav } 154761efaa7SDag-Erling Smørgrav 155761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_READ) 156761efaa7SDag-Erling Smørgrav PAPPEND("READ") 157761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_WRITE) 158761efaa7SDag-Erling Smørgrav PAPPEND("WRITE") 159761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_CREAT) 160761efaa7SDag-Erling Smørgrav PAPPEND("CREATE") 161761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_TRUNC) 162761efaa7SDag-Erling Smørgrav PAPPEND("TRUNCATE") 163761efaa7SDag-Erling Smørgrav if (pflags & SSH2_FXF_EXCL) 164761efaa7SDag-Erling Smørgrav PAPPEND("EXCL") 165761efaa7SDag-Erling Smørgrav 166761efaa7SDag-Erling Smørgrav return ret; 167761efaa7SDag-Erling Smørgrav } 168761efaa7SDag-Erling Smørgrav 169ae1f160dSDag-Erling Smørgrav static Attrib * 170b66f2d16SKris Kennaway get_attrib(void) 171b66f2d16SKris Kennaway { 172b66f2d16SKris Kennaway return decode_attrib(&iqueue); 173b66f2d16SKris Kennaway } 174b66f2d16SKris Kennaway 175b66f2d16SKris Kennaway /* handle handles */ 176b66f2d16SKris Kennaway 177b66f2d16SKris Kennaway typedef struct Handle Handle; 178b66f2d16SKris Kennaway struct Handle { 179b66f2d16SKris Kennaway int use; 180b66f2d16SKris Kennaway DIR *dirp; 181b66f2d16SKris Kennaway int fd; 182b66f2d16SKris Kennaway char *name; 183761efaa7SDag-Erling Smørgrav u_int64_t bytes_read, bytes_write; 184d4af9e69SDag-Erling Smørgrav int next_unused; 185b66f2d16SKris Kennaway }; 1861e8db6e2SBrian Feldman 187b66f2d16SKris Kennaway enum { 188b66f2d16SKris Kennaway HANDLE_UNUSED, 189b66f2d16SKris Kennaway HANDLE_DIR, 190b66f2d16SKris Kennaway HANDLE_FILE 191b66f2d16SKris Kennaway }; 1921e8db6e2SBrian Feldman 193d4af9e69SDag-Erling Smørgrav Handle *handles = NULL; 194d4af9e69SDag-Erling Smørgrav u_int num_handles = 0; 195d4af9e69SDag-Erling Smørgrav int first_unused_handle = -1; 196b66f2d16SKris Kennaway 197d4af9e69SDag-Erling Smørgrav static void handle_unused(int i) 198b66f2d16SKris Kennaway { 199b66f2d16SKris Kennaway handles[i].use = HANDLE_UNUSED; 200d4af9e69SDag-Erling Smørgrav handles[i].next_unused = first_unused_handle; 201d4af9e69SDag-Erling Smørgrav first_unused_handle = i; 202b66f2d16SKris Kennaway } 203b66f2d16SKris Kennaway 204ae1f160dSDag-Erling Smørgrav static int 205efcad6b7SDag-Erling Smørgrav handle_new(int use, const char *name, int fd, DIR *dirp) 206b66f2d16SKris Kennaway { 207d4af9e69SDag-Erling Smørgrav int i; 2081e8db6e2SBrian Feldman 209d4af9e69SDag-Erling Smørgrav if (first_unused_handle == -1) { 210d4af9e69SDag-Erling Smørgrav if (num_handles + 1 <= num_handles) 211d4af9e69SDag-Erling Smørgrav return -1; 212d4af9e69SDag-Erling Smørgrav num_handles++; 213d4af9e69SDag-Erling Smørgrav handles = xrealloc(handles, num_handles, sizeof(Handle)); 214d4af9e69SDag-Erling Smørgrav handle_unused(num_handles - 1); 215d4af9e69SDag-Erling Smørgrav } 216d4af9e69SDag-Erling Smørgrav 217d4af9e69SDag-Erling Smørgrav i = first_unused_handle; 218d4af9e69SDag-Erling Smørgrav first_unused_handle = handles[i].next_unused; 219d4af9e69SDag-Erling Smørgrav 220b66f2d16SKris Kennaway handles[i].use = use; 221b66f2d16SKris Kennaway handles[i].dirp = dirp; 222b66f2d16SKris Kennaway handles[i].fd = fd; 223d0c8c0bcSDag-Erling Smørgrav handles[i].name = xstrdup(name); 224761efaa7SDag-Erling Smørgrav handles[i].bytes_read = handles[i].bytes_write = 0; 225d4af9e69SDag-Erling Smørgrav 226b66f2d16SKris Kennaway return i; 227b66f2d16SKris Kennaway } 228b66f2d16SKris Kennaway 229ae1f160dSDag-Erling Smørgrav static int 230b66f2d16SKris Kennaway handle_is_ok(int i, int type) 231b66f2d16SKris Kennaway { 232d4af9e69SDag-Erling Smørgrav return i >= 0 && (u_int)i < num_handles && handles[i].use == type; 233b66f2d16SKris Kennaway } 234b66f2d16SKris Kennaway 235ae1f160dSDag-Erling Smørgrav static int 236b66f2d16SKris Kennaway handle_to_string(int handle, char **stringp, int *hlenp) 237b66f2d16SKris Kennaway { 238b66f2d16SKris Kennaway if (stringp == NULL || hlenp == NULL) 239b66f2d16SKris Kennaway return -1; 2401e8db6e2SBrian Feldman *stringp = xmalloc(sizeof(int32_t)); 241761efaa7SDag-Erling Smørgrav put_u32(*stringp, handle); 2421e8db6e2SBrian Feldman *hlenp = sizeof(int32_t); 243b66f2d16SKris Kennaway return 0; 244b66f2d16SKris Kennaway } 245b66f2d16SKris Kennaway 246ae1f160dSDag-Erling Smørgrav static int 247efcad6b7SDag-Erling Smørgrav handle_from_string(const char *handle, u_int hlen) 248b66f2d16SKris Kennaway { 2491e8db6e2SBrian Feldman int val; 2501e8db6e2SBrian Feldman 2511e8db6e2SBrian Feldman if (hlen != sizeof(int32_t)) 252b66f2d16SKris Kennaway return -1; 253761efaa7SDag-Erling Smørgrav val = get_u32(handle); 254b66f2d16SKris Kennaway if (handle_is_ok(val, HANDLE_FILE) || 255b66f2d16SKris Kennaway handle_is_ok(val, HANDLE_DIR)) 256b66f2d16SKris Kennaway return val; 257b66f2d16SKris Kennaway return -1; 258b66f2d16SKris Kennaway } 259b66f2d16SKris Kennaway 260ae1f160dSDag-Erling Smørgrav static char * 261b66f2d16SKris Kennaway handle_to_name(int handle) 262b66f2d16SKris Kennaway { 263b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)|| 264b66f2d16SKris Kennaway handle_is_ok(handle, HANDLE_FILE)) 265b66f2d16SKris Kennaway return handles[handle].name; 266b66f2d16SKris Kennaway return NULL; 267b66f2d16SKris Kennaway } 268b66f2d16SKris Kennaway 269ae1f160dSDag-Erling Smørgrav static DIR * 270b66f2d16SKris Kennaway handle_to_dir(int handle) 271b66f2d16SKris Kennaway { 272b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_DIR)) 273b66f2d16SKris Kennaway return handles[handle].dirp; 274b66f2d16SKris Kennaway return NULL; 275b66f2d16SKris Kennaway } 276b66f2d16SKris Kennaway 277ae1f160dSDag-Erling Smørgrav static int 278b66f2d16SKris Kennaway handle_to_fd(int handle) 279b66f2d16SKris Kennaway { 280b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) 281b66f2d16SKris Kennaway return handles[handle].fd; 282b66f2d16SKris Kennaway return -1; 283b66f2d16SKris Kennaway } 284b66f2d16SKris Kennaway 285761efaa7SDag-Erling Smørgrav static void 286761efaa7SDag-Erling Smørgrav handle_update_read(int handle, ssize_t bytes) 287761efaa7SDag-Erling Smørgrav { 288761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 289761efaa7SDag-Erling Smørgrav handles[handle].bytes_read += bytes; 290761efaa7SDag-Erling Smørgrav } 291761efaa7SDag-Erling Smørgrav 292761efaa7SDag-Erling Smørgrav static void 293761efaa7SDag-Erling Smørgrav handle_update_write(int handle, ssize_t bytes) 294761efaa7SDag-Erling Smørgrav { 295761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 296761efaa7SDag-Erling Smørgrav handles[handle].bytes_write += bytes; 297761efaa7SDag-Erling Smørgrav } 298761efaa7SDag-Erling Smørgrav 299761efaa7SDag-Erling Smørgrav static u_int64_t 300761efaa7SDag-Erling Smørgrav handle_bytes_read(int handle) 301761efaa7SDag-Erling Smørgrav { 302761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 303761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_read); 304761efaa7SDag-Erling Smørgrav return 0; 305761efaa7SDag-Erling Smørgrav } 306761efaa7SDag-Erling Smørgrav 307761efaa7SDag-Erling Smørgrav static u_int64_t 308761efaa7SDag-Erling Smørgrav handle_bytes_write(int handle) 309761efaa7SDag-Erling Smørgrav { 310761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) 311761efaa7SDag-Erling Smørgrav return (handles[handle].bytes_write); 312761efaa7SDag-Erling Smørgrav return 0; 313761efaa7SDag-Erling Smørgrav } 314761efaa7SDag-Erling Smørgrav 315ae1f160dSDag-Erling Smørgrav static int 316b66f2d16SKris Kennaway handle_close(int handle) 317b66f2d16SKris Kennaway { 318b66f2d16SKris Kennaway int ret = -1; 3191e8db6e2SBrian Feldman 320b66f2d16SKris Kennaway if (handle_is_ok(handle, HANDLE_FILE)) { 321b66f2d16SKris Kennaway ret = close(handles[handle].fd); 322*e4a9863fSDag-Erling Smørgrav free(handles[handle].name); 323d4af9e69SDag-Erling Smørgrav handle_unused(handle); 324b66f2d16SKris Kennaway } else if (handle_is_ok(handle, HANDLE_DIR)) { 325b66f2d16SKris Kennaway ret = closedir(handles[handle].dirp); 326*e4a9863fSDag-Erling Smørgrav free(handles[handle].name); 327d4af9e69SDag-Erling Smørgrav handle_unused(handle); 328b66f2d16SKris Kennaway } else { 329b66f2d16SKris Kennaway errno = ENOENT; 330b66f2d16SKris Kennaway } 331b66f2d16SKris Kennaway return ret; 332b66f2d16SKris Kennaway } 333b66f2d16SKris Kennaway 334761efaa7SDag-Erling Smørgrav static void 335761efaa7SDag-Erling Smørgrav handle_log_close(int handle, char *emsg) 336761efaa7SDag-Erling Smørgrav { 337761efaa7SDag-Erling Smørgrav if (handle_is_ok(handle, HANDLE_FILE)) { 338761efaa7SDag-Erling Smørgrav logit("%s%sclose \"%s\" bytes read %llu written %llu", 339761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 340761efaa7SDag-Erling Smørgrav handle_to_name(handle), 341d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_read(handle), 342d4af9e69SDag-Erling Smørgrav (unsigned long long)handle_bytes_write(handle)); 343761efaa7SDag-Erling Smørgrav } else { 344761efaa7SDag-Erling Smørgrav logit("%s%sclosedir \"%s\"", 345761efaa7SDag-Erling Smørgrav emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 346761efaa7SDag-Erling Smørgrav handle_to_name(handle)); 347761efaa7SDag-Erling Smørgrav } 348761efaa7SDag-Erling Smørgrav } 349761efaa7SDag-Erling Smørgrav 350761efaa7SDag-Erling Smørgrav static void 351761efaa7SDag-Erling Smørgrav handle_log_exit(void) 352761efaa7SDag-Erling Smørgrav { 353761efaa7SDag-Erling Smørgrav u_int i; 354761efaa7SDag-Erling Smørgrav 355d4af9e69SDag-Erling Smørgrav for (i = 0; i < num_handles; i++) 356761efaa7SDag-Erling Smørgrav if (handles[i].use != HANDLE_UNUSED) 357761efaa7SDag-Erling Smørgrav handle_log_close(i, "forced"); 358761efaa7SDag-Erling Smørgrav } 359761efaa7SDag-Erling Smørgrav 360ae1f160dSDag-Erling Smørgrav static int 361b66f2d16SKris Kennaway get_handle(void) 362b66f2d16SKris Kennaway { 363b66f2d16SKris Kennaway char *handle; 3641e8db6e2SBrian Feldman int val = -1; 365b66f2d16SKris Kennaway u_int hlen; 3661e8db6e2SBrian Feldman 367b66f2d16SKris Kennaway handle = get_string(&hlen); 3681e8db6e2SBrian Feldman if (hlen < 256) 369b66f2d16SKris Kennaway val = handle_from_string(handle, hlen); 370*e4a9863fSDag-Erling Smørgrav free(handle); 371b66f2d16SKris Kennaway return val; 372b66f2d16SKris Kennaway } 373b66f2d16SKris Kennaway 374b66f2d16SKris Kennaway /* send replies */ 375b66f2d16SKris Kennaway 376ae1f160dSDag-Erling Smørgrav static void 377b66f2d16SKris Kennaway send_msg(Buffer *m) 378b66f2d16SKris Kennaway { 379b66f2d16SKris Kennaway int mlen = buffer_len(m); 3801e8db6e2SBrian Feldman 381b66f2d16SKris Kennaway buffer_put_int(&oqueue, mlen); 382b66f2d16SKris Kennaway buffer_append(&oqueue, buffer_ptr(m), mlen); 383b66f2d16SKris Kennaway buffer_consume(m, mlen); 384b66f2d16SKris Kennaway } 385b66f2d16SKris Kennaway 386761efaa7SDag-Erling Smørgrav static const char * 387761efaa7SDag-Erling Smørgrav status_to_message(u_int32_t status) 388b66f2d16SKris Kennaway { 3891e8db6e2SBrian Feldman const char *status_messages[] = { 3901e8db6e2SBrian Feldman "Success", /* SSH_FX_OK */ 3911e8db6e2SBrian Feldman "End of file", /* SSH_FX_EOF */ 3921e8db6e2SBrian Feldman "No such file", /* SSH_FX_NO_SUCH_FILE */ 3931e8db6e2SBrian Feldman "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 3941e8db6e2SBrian Feldman "Failure", /* SSH_FX_FAILURE */ 3951e8db6e2SBrian Feldman "Bad message", /* SSH_FX_BAD_MESSAGE */ 3961e8db6e2SBrian Feldman "No connection", /* SSH_FX_NO_CONNECTION */ 3971e8db6e2SBrian Feldman "Connection lost", /* SSH_FX_CONNECTION_LOST */ 3981e8db6e2SBrian Feldman "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 3991e8db6e2SBrian Feldman "Unknown error" /* Others */ 4001e8db6e2SBrian Feldman }; 401761efaa7SDag-Erling Smørgrav return (status_messages[MIN(status,SSH2_FX_MAX)]); 402761efaa7SDag-Erling Smørgrav } 4031e8db6e2SBrian Feldman 404761efaa7SDag-Erling Smørgrav static void 405761efaa7SDag-Erling Smørgrav send_status(u_int32_t id, u_int32_t status) 406761efaa7SDag-Erling Smørgrav { 407761efaa7SDag-Erling Smørgrav Buffer msg; 408761efaa7SDag-Erling Smørgrav 409761efaa7SDag-Erling Smørgrav debug3("request %u: sent status %u", id, status); 410761efaa7SDag-Erling Smørgrav if (log_level > SYSLOG_LEVEL_VERBOSE || 411761efaa7SDag-Erling Smørgrav (status != SSH2_FX_OK && status != SSH2_FX_EOF)) 412761efaa7SDag-Erling Smørgrav logit("sent status %s", status_to_message(status)); 413b66f2d16SKris Kennaway buffer_init(&msg); 4141e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_STATUS); 415b66f2d16SKris Kennaway buffer_put_int(&msg, id); 416d74d50a8SDag-Erling Smørgrav buffer_put_int(&msg, status); 4171e8db6e2SBrian Feldman if (version >= 3) { 418761efaa7SDag-Erling Smørgrav buffer_put_cstring(&msg, status_to_message(status)); 4191e8db6e2SBrian Feldman buffer_put_cstring(&msg, ""); 4201e8db6e2SBrian Feldman } 421b66f2d16SKris Kennaway send_msg(&msg); 422b66f2d16SKris Kennaway buffer_free(&msg); 423b66f2d16SKris Kennaway } 424ae1f160dSDag-Erling Smørgrav static void 425efcad6b7SDag-Erling Smørgrav send_data_or_handle(char type, u_int32_t id, const char *data, int dlen) 426b66f2d16SKris Kennaway { 427b66f2d16SKris Kennaway Buffer msg; 4281e8db6e2SBrian Feldman 429b66f2d16SKris Kennaway buffer_init(&msg); 430b66f2d16SKris Kennaway buffer_put_char(&msg, type); 431b66f2d16SKris Kennaway buffer_put_int(&msg, id); 432b66f2d16SKris Kennaway buffer_put_string(&msg, data, dlen); 433b66f2d16SKris Kennaway send_msg(&msg); 434b66f2d16SKris Kennaway buffer_free(&msg); 435b66f2d16SKris Kennaway } 436b66f2d16SKris Kennaway 437ae1f160dSDag-Erling Smørgrav static void 438efcad6b7SDag-Erling Smørgrav send_data(u_int32_t id, const char *data, int dlen) 439b66f2d16SKris Kennaway { 440761efaa7SDag-Erling Smørgrav debug("request %u: sent data len %d", id, dlen); 4411e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 442b66f2d16SKris Kennaway } 443b66f2d16SKris Kennaway 444ae1f160dSDag-Erling Smørgrav static void 445b66f2d16SKris Kennaway send_handle(u_int32_t id, int handle) 446b66f2d16SKris Kennaway { 447b66f2d16SKris Kennaway char *string; 448b66f2d16SKris Kennaway int hlen; 4491e8db6e2SBrian Feldman 450b66f2d16SKris Kennaway handle_to_string(handle, &string, &hlen); 451761efaa7SDag-Erling Smørgrav debug("request %u: sent handle handle %d", id, handle); 4521e8db6e2SBrian Feldman send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 453*e4a9863fSDag-Erling Smørgrav free(string); 454b66f2d16SKris Kennaway } 455b66f2d16SKris Kennaway 456ae1f160dSDag-Erling Smørgrav static void 457efcad6b7SDag-Erling Smørgrav send_names(u_int32_t id, int count, const Stat *stats) 458b66f2d16SKris Kennaway { 459b66f2d16SKris Kennaway Buffer msg; 460b66f2d16SKris Kennaway int i; 4611e8db6e2SBrian Feldman 462b66f2d16SKris Kennaway buffer_init(&msg); 4631e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_NAME); 464b66f2d16SKris Kennaway buffer_put_int(&msg, id); 465b66f2d16SKris Kennaway buffer_put_int(&msg, count); 466761efaa7SDag-Erling Smørgrav debug("request %u: sent names count %d", id, count); 467b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 468b66f2d16SKris Kennaway buffer_put_cstring(&msg, stats[i].name); 469b66f2d16SKris Kennaway buffer_put_cstring(&msg, stats[i].long_name); 470b66f2d16SKris Kennaway encode_attrib(&msg, &stats[i].attrib); 471b66f2d16SKris Kennaway } 472b66f2d16SKris Kennaway send_msg(&msg); 473b66f2d16SKris Kennaway buffer_free(&msg); 474b66f2d16SKris Kennaway } 475b66f2d16SKris Kennaway 476ae1f160dSDag-Erling Smørgrav static void 477efcad6b7SDag-Erling Smørgrav send_attrib(u_int32_t id, const Attrib *a) 478b66f2d16SKris Kennaway { 479b66f2d16SKris Kennaway Buffer msg; 4801e8db6e2SBrian Feldman 481761efaa7SDag-Erling Smørgrav debug("request %u: sent attrib have 0x%x", id, a->flags); 482b66f2d16SKris Kennaway buffer_init(&msg); 4831e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_ATTRS); 484b66f2d16SKris Kennaway buffer_put_int(&msg, id); 485b66f2d16SKris Kennaway encode_attrib(&msg, a); 486b66f2d16SKris Kennaway send_msg(&msg); 487b66f2d16SKris Kennaway buffer_free(&msg); 488b66f2d16SKris Kennaway } 489b66f2d16SKris Kennaway 490d4af9e69SDag-Erling Smørgrav static void 491d4af9e69SDag-Erling Smørgrav send_statvfs(u_int32_t id, struct statvfs *st) 492d4af9e69SDag-Erling Smørgrav { 493d4af9e69SDag-Erling Smørgrav Buffer msg; 494d4af9e69SDag-Erling Smørgrav u_int64_t flag; 495d4af9e69SDag-Erling Smørgrav 496d4af9e69SDag-Erling Smørgrav flag = (st->f_flag & ST_RDONLY) ? SSH2_FXE_STATVFS_ST_RDONLY : 0; 497d4af9e69SDag-Erling Smørgrav flag |= (st->f_flag & ST_NOSUID) ? SSH2_FXE_STATVFS_ST_NOSUID : 0; 498d4af9e69SDag-Erling Smørgrav 499d4af9e69SDag-Erling Smørgrav buffer_init(&msg); 500d4af9e69SDag-Erling Smørgrav buffer_put_char(&msg, SSH2_FXP_EXTENDED_REPLY); 501d4af9e69SDag-Erling Smørgrav buffer_put_int(&msg, id); 502d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_bsize); 503d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_frsize); 504d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_blocks); 505d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_bfree); 506d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_bavail); 507d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_files); 508d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_ffree); 509d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_favail); 510d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, FSID_TO_ULONG(st->f_fsid)); 511d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, flag); 512d4af9e69SDag-Erling Smørgrav buffer_put_int64(&msg, st->f_namemax); 513d4af9e69SDag-Erling Smørgrav send_msg(&msg); 514d4af9e69SDag-Erling Smørgrav buffer_free(&msg); 515d4af9e69SDag-Erling Smørgrav } 516d4af9e69SDag-Erling Smørgrav 517b66f2d16SKris Kennaway /* parse incoming */ 518b66f2d16SKris Kennaway 519ae1f160dSDag-Erling Smørgrav static void 520b66f2d16SKris Kennaway process_init(void) 521b66f2d16SKris Kennaway { 522b66f2d16SKris Kennaway Buffer msg; 523b66f2d16SKris Kennaway 524545d5ecaSDag-Erling Smørgrav version = get_int(); 525e146993eSDag-Erling Smørgrav verbose("received client version %u", version); 526b66f2d16SKris Kennaway buffer_init(&msg); 5271e8db6e2SBrian Feldman buffer_put_char(&msg, SSH2_FXP_VERSION); 5281e8db6e2SBrian Feldman buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 529d4af9e69SDag-Erling Smørgrav /* POSIX rename extension */ 530d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "posix-rename@openssh.com"); 531d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "1"); /* version */ 532d4af9e69SDag-Erling Smørgrav /* statvfs extension */ 533d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "statvfs@openssh.com"); 534d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "2"); /* version */ 535d4af9e69SDag-Erling Smørgrav /* fstatvfs extension */ 536d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "fstatvfs@openssh.com"); 537d4af9e69SDag-Erling Smørgrav buffer_put_cstring(&msg, "2"); /* version */ 5384a421b63SDag-Erling Smørgrav /* hardlink extension */ 5394a421b63SDag-Erling Smørgrav buffer_put_cstring(&msg, "hardlink@openssh.com"); 5404a421b63SDag-Erling Smørgrav buffer_put_cstring(&msg, "1"); /* version */ 541b66f2d16SKris Kennaway send_msg(&msg); 542b66f2d16SKris Kennaway buffer_free(&msg); 543b66f2d16SKris Kennaway } 544b66f2d16SKris Kennaway 545ae1f160dSDag-Erling Smørgrav static void 546b66f2d16SKris Kennaway process_open(void) 547b66f2d16SKris Kennaway { 548b66f2d16SKris Kennaway u_int32_t id, pflags; 549b66f2d16SKris Kennaway Attrib *a; 550b66f2d16SKris Kennaway char *name; 5511e8db6e2SBrian Feldman int handle, fd, flags, mode, status = SSH2_FX_FAILURE; 552b66f2d16SKris Kennaway 553b66f2d16SKris Kennaway id = get_int(); 554b66f2d16SKris Kennaway name = get_string(NULL); 5551e8db6e2SBrian Feldman pflags = get_int(); /* portable flags */ 556761efaa7SDag-Erling Smørgrav debug3("request %u: open flags %d", id, pflags); 557b66f2d16SKris Kennaway a = get_attrib(); 558b66f2d16SKris Kennaway flags = flags_from_portable(pflags); 5591e8db6e2SBrian Feldman mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; 560761efaa7SDag-Erling Smørgrav logit("open \"%s\" flags %s mode 0%o", 561761efaa7SDag-Erling Smørgrav name, string_from_portable(pflags), mode); 562b15c8340SDag-Erling Smørgrav if (readonly && 563b15c8340SDag-Erling Smørgrav ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR)) 564b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 565b15c8340SDag-Erling Smørgrav else { 566b66f2d16SKris Kennaway fd = open(name, flags, mode); 567b66f2d16SKris Kennaway if (fd < 0) { 568b66f2d16SKris Kennaway status = errno_to_portable(errno); 569b66f2d16SKris Kennaway } else { 570d0c8c0bcSDag-Erling Smørgrav handle = handle_new(HANDLE_FILE, name, fd, NULL); 571b66f2d16SKris Kennaway if (handle < 0) { 572b66f2d16SKris Kennaway close(fd); 573b66f2d16SKris Kennaway } else { 574b66f2d16SKris Kennaway send_handle(id, handle); 5751e8db6e2SBrian Feldman status = SSH2_FX_OK; 576b66f2d16SKris Kennaway } 577b66f2d16SKris Kennaway } 578b15c8340SDag-Erling Smørgrav } 5791e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 580b66f2d16SKris Kennaway send_status(id, status); 581*e4a9863fSDag-Erling Smørgrav free(name); 582b66f2d16SKris Kennaway } 583b66f2d16SKris Kennaway 584ae1f160dSDag-Erling Smørgrav static void 585b66f2d16SKris Kennaway process_close(void) 586b66f2d16SKris Kennaway { 587b66f2d16SKris Kennaway u_int32_t id; 5881e8db6e2SBrian Feldman int handle, ret, status = SSH2_FX_FAILURE; 589b66f2d16SKris Kennaway 590b66f2d16SKris Kennaway id = get_int(); 591b66f2d16SKris Kennaway handle = get_handle(); 592761efaa7SDag-Erling Smørgrav debug3("request %u: close handle %u", id, handle); 593761efaa7SDag-Erling Smørgrav handle_log_close(handle, NULL); 594b66f2d16SKris Kennaway ret = handle_close(handle); 5951e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 596b66f2d16SKris Kennaway send_status(id, status); 597b66f2d16SKris Kennaway } 598b66f2d16SKris Kennaway 599ae1f160dSDag-Erling Smørgrav static void 600b66f2d16SKris Kennaway process_read(void) 601b66f2d16SKris Kennaway { 602b66f2d16SKris Kennaway char buf[64*1024]; 6031e8db6e2SBrian Feldman u_int32_t id, len; 6041e8db6e2SBrian Feldman int handle, fd, ret, status = SSH2_FX_FAILURE; 605b66f2d16SKris Kennaway u_int64_t off; 606b66f2d16SKris Kennaway 607b66f2d16SKris Kennaway id = get_int(); 608b66f2d16SKris Kennaway handle = get_handle(); 6091e8db6e2SBrian Feldman off = get_int64(); 610b66f2d16SKris Kennaway len = get_int(); 611b66f2d16SKris Kennaway 612761efaa7SDag-Erling Smørgrav debug("request %u: read \"%s\" (handle %d) off %llu len %d", 613761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len); 614b66f2d16SKris Kennaway if (len > sizeof buf) { 615b66f2d16SKris Kennaway len = sizeof buf; 616761efaa7SDag-Erling Smørgrav debug2("read change len %d", len); 617b66f2d16SKris Kennaway } 618b66f2d16SKris Kennaway fd = handle_to_fd(handle); 619b66f2d16SKris Kennaway if (fd >= 0) { 620b66f2d16SKris Kennaway if (lseek(fd, off, SEEK_SET) < 0) { 621b66f2d16SKris Kennaway error("process_read: seek failed"); 622b66f2d16SKris Kennaway status = errno_to_portable(errno); 623b66f2d16SKris Kennaway } else { 624b66f2d16SKris Kennaway ret = read(fd, buf, len); 625b66f2d16SKris Kennaway if (ret < 0) { 626b66f2d16SKris Kennaway status = errno_to_portable(errno); 627b66f2d16SKris Kennaway } else if (ret == 0) { 6281e8db6e2SBrian Feldman status = SSH2_FX_EOF; 629b66f2d16SKris Kennaway } else { 630b66f2d16SKris Kennaway send_data(id, buf, ret); 6311e8db6e2SBrian Feldman status = SSH2_FX_OK; 632761efaa7SDag-Erling Smørgrav handle_update_read(handle, ret); 633b66f2d16SKris Kennaway } 634b66f2d16SKris Kennaway } 635b66f2d16SKris Kennaway } 6361e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 637b66f2d16SKris Kennaway send_status(id, status); 638b66f2d16SKris Kennaway } 639b66f2d16SKris Kennaway 640ae1f160dSDag-Erling Smørgrav static void 641b66f2d16SKris Kennaway process_write(void) 642b66f2d16SKris Kennaway { 6431e8db6e2SBrian Feldman u_int32_t id; 644b66f2d16SKris Kennaway u_int64_t off; 645b66f2d16SKris Kennaway u_int len; 646b15c8340SDag-Erling Smørgrav int handle, fd, ret, status; 647b66f2d16SKris Kennaway char *data; 648b66f2d16SKris Kennaway 649b66f2d16SKris Kennaway id = get_int(); 650b66f2d16SKris Kennaway handle = get_handle(); 6511e8db6e2SBrian Feldman off = get_int64(); 652b66f2d16SKris Kennaway data = get_string(&len); 653b66f2d16SKris Kennaway 654761efaa7SDag-Erling Smørgrav debug("request %u: write \"%s\" (handle %d) off %llu len %d", 655761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle, (unsigned long long)off, len); 656b66f2d16SKris Kennaway fd = handle_to_fd(handle); 657b15c8340SDag-Erling Smørgrav 658b15c8340SDag-Erling Smørgrav if (fd < 0) 659b15c8340SDag-Erling Smørgrav status = SSH2_FX_FAILURE; 660b15c8340SDag-Erling Smørgrav else if (readonly) 661b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 662b15c8340SDag-Erling Smørgrav else { 663b66f2d16SKris Kennaway if (lseek(fd, off, SEEK_SET) < 0) { 664b66f2d16SKris Kennaway status = errno_to_portable(errno); 665b66f2d16SKris Kennaway error("process_write: seek failed"); 666b66f2d16SKris Kennaway } else { 667b66f2d16SKris Kennaway /* XXX ATOMICIO ? */ 668b66f2d16SKris Kennaway ret = write(fd, data, len); 669043840dfSDag-Erling Smørgrav if (ret < 0) { 670b66f2d16SKris Kennaway error("process_write: write failed"); 671b66f2d16SKris Kennaway status = errno_to_portable(errno); 672043840dfSDag-Erling Smørgrav } else if ((size_t)ret == len) { 6731e8db6e2SBrian Feldman status = SSH2_FX_OK; 674761efaa7SDag-Erling Smørgrav handle_update_write(handle, ret); 675b66f2d16SKris Kennaway } else { 676761efaa7SDag-Erling Smørgrav debug2("nothing at all written"); 677b15c8340SDag-Erling Smørgrav status = SSH2_FX_FAILURE; 678b66f2d16SKris Kennaway } 679b66f2d16SKris Kennaway } 680b66f2d16SKris Kennaway } 681b66f2d16SKris Kennaway send_status(id, status); 682*e4a9863fSDag-Erling Smørgrav free(data); 683b66f2d16SKris Kennaway } 684b66f2d16SKris Kennaway 685ae1f160dSDag-Erling Smørgrav static void 686b66f2d16SKris Kennaway process_do_stat(int do_lstat) 687b66f2d16SKris Kennaway { 6881e8db6e2SBrian Feldman Attrib a; 689b66f2d16SKris Kennaway struct stat st; 690b66f2d16SKris Kennaway u_int32_t id; 691b66f2d16SKris Kennaway char *name; 6921e8db6e2SBrian Feldman int ret, status = SSH2_FX_FAILURE; 693b66f2d16SKris Kennaway 694b66f2d16SKris Kennaway id = get_int(); 695b66f2d16SKris Kennaway name = get_string(NULL); 696761efaa7SDag-Erling Smørgrav debug3("request %u: %sstat", id, do_lstat ? "l" : ""); 697761efaa7SDag-Erling Smørgrav verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); 698b66f2d16SKris Kennaway ret = do_lstat ? lstat(name, &st) : stat(name, &st); 699b66f2d16SKris Kennaway if (ret < 0) { 700b66f2d16SKris Kennaway status = errno_to_portable(errno); 701b66f2d16SKris Kennaway } else { 7021e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 7031e8db6e2SBrian Feldman send_attrib(id, &a); 7041e8db6e2SBrian Feldman status = SSH2_FX_OK; 705b66f2d16SKris Kennaway } 7061e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 707b66f2d16SKris Kennaway send_status(id, status); 708*e4a9863fSDag-Erling Smørgrav free(name); 709b66f2d16SKris Kennaway } 710b66f2d16SKris Kennaway 711ae1f160dSDag-Erling Smørgrav static void 712b66f2d16SKris Kennaway process_stat(void) 713b66f2d16SKris Kennaway { 714b66f2d16SKris Kennaway process_do_stat(0); 715b66f2d16SKris Kennaway } 716b66f2d16SKris Kennaway 717ae1f160dSDag-Erling Smørgrav static void 718b66f2d16SKris Kennaway process_lstat(void) 719b66f2d16SKris Kennaway { 720b66f2d16SKris Kennaway process_do_stat(1); 721b66f2d16SKris Kennaway } 722b66f2d16SKris Kennaway 723ae1f160dSDag-Erling Smørgrav static void 724b66f2d16SKris Kennaway process_fstat(void) 725b66f2d16SKris Kennaway { 7261e8db6e2SBrian Feldman Attrib a; 727b66f2d16SKris Kennaway struct stat st; 728b66f2d16SKris Kennaway u_int32_t id; 7291e8db6e2SBrian Feldman int fd, ret, handle, status = SSH2_FX_FAILURE; 730b66f2d16SKris Kennaway 731b66f2d16SKris Kennaway id = get_int(); 732b66f2d16SKris Kennaway handle = get_handle(); 733761efaa7SDag-Erling Smørgrav debug("request %u: fstat \"%s\" (handle %u)", 734761efaa7SDag-Erling Smørgrav id, handle_to_name(handle), handle); 735b66f2d16SKris Kennaway fd = handle_to_fd(handle); 736b66f2d16SKris Kennaway if (fd >= 0) { 737b66f2d16SKris Kennaway ret = fstat(fd, &st); 738b66f2d16SKris Kennaway if (ret < 0) { 739b66f2d16SKris Kennaway status = errno_to_portable(errno); 740b66f2d16SKris Kennaway } else { 7411e8db6e2SBrian Feldman stat_to_attrib(&st, &a); 7421e8db6e2SBrian Feldman send_attrib(id, &a); 7431e8db6e2SBrian Feldman status = SSH2_FX_OK; 744b66f2d16SKris Kennaway } 745b66f2d16SKris Kennaway } 7461e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 747b66f2d16SKris Kennaway send_status(id, status); 748b66f2d16SKris Kennaway } 749b66f2d16SKris Kennaway 750ae1f160dSDag-Erling Smørgrav static struct timeval * 751efcad6b7SDag-Erling Smørgrav attrib_to_tv(const Attrib *a) 752b66f2d16SKris Kennaway { 753b66f2d16SKris Kennaway static struct timeval tv[2]; 7541e8db6e2SBrian Feldman 755b66f2d16SKris Kennaway tv[0].tv_sec = a->atime; 756b66f2d16SKris Kennaway tv[0].tv_usec = 0; 757b66f2d16SKris Kennaway tv[1].tv_sec = a->mtime; 758b66f2d16SKris Kennaway tv[1].tv_usec = 0; 759b66f2d16SKris Kennaway return tv; 760b66f2d16SKris Kennaway } 761b66f2d16SKris Kennaway 762ae1f160dSDag-Erling Smørgrav static void 763b66f2d16SKris Kennaway process_setstat(void) 764b66f2d16SKris Kennaway { 765b66f2d16SKris Kennaway Attrib *a; 766b66f2d16SKris Kennaway u_int32_t id; 767b66f2d16SKris Kennaway char *name; 768ee21a45fSDag-Erling Smørgrav int status = SSH2_FX_OK, ret; 769b66f2d16SKris Kennaway 770b66f2d16SKris Kennaway id = get_int(); 771b66f2d16SKris Kennaway name = get_string(NULL); 772b66f2d16SKris Kennaway a = get_attrib(); 773761efaa7SDag-Erling Smørgrav debug("request %u: setstat name \"%s\"", id, name); 774b15c8340SDag-Erling Smørgrav if (readonly) { 775b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 776b15c8340SDag-Erling Smørgrav a->flags = 0; 777b15c8340SDag-Erling Smørgrav } 778ae1f160dSDag-Erling Smørgrav if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 779d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu", 780d4af9e69SDag-Erling Smørgrav name, (unsigned long long)a->size); 781ae1f160dSDag-Erling Smørgrav ret = truncate(name, a->size); 782ae1f160dSDag-Erling Smørgrav if (ret == -1) 783ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 784ae1f160dSDag-Erling Smørgrav } 7851e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 786761efaa7SDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a->perm); 787d4af9e69SDag-Erling Smørgrav ret = chmod(name, a->perm & 07777); 788b66f2d16SKris Kennaway if (ret == -1) 789b66f2d16SKris Kennaway status = errno_to_portable(errno); 790b66f2d16SKris Kennaway } 7911e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 792761efaa7SDag-Erling Smørgrav char buf[64]; 793761efaa7SDag-Erling Smørgrav time_t t = a->mtime; 794761efaa7SDag-Erling Smørgrav 795761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 796761efaa7SDag-Erling Smørgrav localtime(&t)); 797761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf); 798b66f2d16SKris Kennaway ret = utimes(name, attrib_to_tv(a)); 799b66f2d16SKris Kennaway if (ret == -1) 800b66f2d16SKris Kennaway status = errno_to_portable(errno); 801b66f2d16SKris Kennaway } 8021e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 803761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name, 804761efaa7SDag-Erling Smørgrav (u_long)a->uid, (u_long)a->gid); 8051e8db6e2SBrian Feldman ret = chown(name, a->uid, a->gid); 8061e8db6e2SBrian Feldman if (ret == -1) 8071e8db6e2SBrian Feldman status = errno_to_portable(errno); 8081e8db6e2SBrian Feldman } 809b66f2d16SKris Kennaway send_status(id, status); 810*e4a9863fSDag-Erling Smørgrav free(name); 811b66f2d16SKris Kennaway } 812b66f2d16SKris Kennaway 813ae1f160dSDag-Erling Smørgrav static void 814b66f2d16SKris Kennaway process_fsetstat(void) 815b66f2d16SKris Kennaway { 816b66f2d16SKris Kennaway Attrib *a; 817b66f2d16SKris Kennaway u_int32_t id; 818b66f2d16SKris Kennaway int handle, fd, ret; 8191e8db6e2SBrian Feldman int status = SSH2_FX_OK; 820b66f2d16SKris Kennaway 821b66f2d16SKris Kennaway id = get_int(); 822b66f2d16SKris Kennaway handle = get_handle(); 823b66f2d16SKris Kennaway a = get_attrib(); 824761efaa7SDag-Erling Smørgrav debug("request %u: fsetstat handle %d", id, handle); 825b66f2d16SKris Kennaway fd = handle_to_fd(handle); 826b15c8340SDag-Erling Smørgrav if (fd < 0) 8271e8db6e2SBrian Feldman status = SSH2_FX_FAILURE; 828b15c8340SDag-Erling Smørgrav else if (readonly) 829b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 830b15c8340SDag-Erling Smørgrav else { 831761efaa7SDag-Erling Smørgrav char *name = handle_to_name(handle); 832761efaa7SDag-Erling Smørgrav 833ae1f160dSDag-Erling Smørgrav if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 834d4af9e69SDag-Erling Smørgrav logit("set \"%s\" size %llu", 835d4af9e69SDag-Erling Smørgrav name, (unsigned long long)a->size); 836ae1f160dSDag-Erling Smørgrav ret = ftruncate(fd, a->size); 837ae1f160dSDag-Erling Smørgrav if (ret == -1) 838ae1f160dSDag-Erling Smørgrav status = errno_to_portable(errno); 839ae1f160dSDag-Erling Smørgrav } 8401e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 841761efaa7SDag-Erling Smørgrav logit("set \"%s\" mode %04o", name, a->perm); 84283d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHMOD 843d4af9e69SDag-Erling Smørgrav ret = fchmod(fd, a->perm & 07777); 84483d2307dSDag-Erling Smørgrav #else 845d4af9e69SDag-Erling Smørgrav ret = chmod(name, a->perm & 07777); 84683d2307dSDag-Erling Smørgrav #endif 847b66f2d16SKris Kennaway if (ret == -1) 848b66f2d16SKris Kennaway status = errno_to_portable(errno); 849b66f2d16SKris Kennaway } 8501e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 851761efaa7SDag-Erling Smørgrav char buf[64]; 852761efaa7SDag-Erling Smørgrav time_t t = a->mtime; 853761efaa7SDag-Erling Smørgrav 854761efaa7SDag-Erling Smørgrav strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 855761efaa7SDag-Erling Smørgrav localtime(&t)); 856761efaa7SDag-Erling Smørgrav logit("set \"%s\" modtime %s", name, buf); 85783d2307dSDag-Erling Smørgrav #ifdef HAVE_FUTIMES 858b66f2d16SKris Kennaway ret = futimes(fd, attrib_to_tv(a)); 85983d2307dSDag-Erling Smørgrav #else 86083d2307dSDag-Erling Smørgrav ret = utimes(name, attrib_to_tv(a)); 86183d2307dSDag-Erling Smørgrav #endif 862b66f2d16SKris Kennaway if (ret == -1) 863b66f2d16SKris Kennaway status = errno_to_portable(errno); 864b66f2d16SKris Kennaway } 8651e8db6e2SBrian Feldman if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 866761efaa7SDag-Erling Smørgrav logit("set \"%s\" owner %lu group %lu", name, 867761efaa7SDag-Erling Smørgrav (u_long)a->uid, (u_long)a->gid); 86883d2307dSDag-Erling Smørgrav #ifdef HAVE_FCHOWN 8691e8db6e2SBrian Feldman ret = fchown(fd, a->uid, a->gid); 87083d2307dSDag-Erling Smørgrav #else 87183d2307dSDag-Erling Smørgrav ret = chown(name, a->uid, a->gid); 87283d2307dSDag-Erling Smørgrav #endif 8731e8db6e2SBrian Feldman if (ret == -1) 8741e8db6e2SBrian Feldman status = errno_to_portable(errno); 8751e8db6e2SBrian Feldman } 876b66f2d16SKris Kennaway } 877b66f2d16SKris Kennaway send_status(id, status); 878b66f2d16SKris Kennaway } 879b66f2d16SKris Kennaway 880ae1f160dSDag-Erling Smørgrav static void 881b66f2d16SKris Kennaway process_opendir(void) 882b66f2d16SKris Kennaway { 883b66f2d16SKris Kennaway DIR *dirp = NULL; 884b66f2d16SKris Kennaway char *path; 8851e8db6e2SBrian Feldman int handle, status = SSH2_FX_FAILURE; 886b66f2d16SKris Kennaway u_int32_t id; 887b66f2d16SKris Kennaway 888b66f2d16SKris Kennaway id = get_int(); 889b66f2d16SKris Kennaway path = get_string(NULL); 890761efaa7SDag-Erling Smørgrav debug3("request %u: opendir", id); 891761efaa7SDag-Erling Smørgrav logit("opendir \"%s\"", path); 892b66f2d16SKris Kennaway dirp = opendir(path); 893b66f2d16SKris Kennaway if (dirp == NULL) { 894b66f2d16SKris Kennaway status = errno_to_portable(errno); 895b66f2d16SKris Kennaway } else { 896d0c8c0bcSDag-Erling Smørgrav handle = handle_new(HANDLE_DIR, path, 0, dirp); 897b66f2d16SKris Kennaway if (handle < 0) { 898b66f2d16SKris Kennaway closedir(dirp); 899b66f2d16SKris Kennaway } else { 900b66f2d16SKris Kennaway send_handle(id, handle); 9011e8db6e2SBrian Feldman status = SSH2_FX_OK; 902b66f2d16SKris Kennaway } 903b66f2d16SKris Kennaway 904b66f2d16SKris Kennaway } 9051e8db6e2SBrian Feldman if (status != SSH2_FX_OK) 906b66f2d16SKris Kennaway send_status(id, status); 907*e4a9863fSDag-Erling Smørgrav free(path); 908b66f2d16SKris Kennaway } 909b66f2d16SKris Kennaway 910ae1f160dSDag-Erling Smørgrav static void 911b66f2d16SKris Kennaway process_readdir(void) 912b66f2d16SKris Kennaway { 913b66f2d16SKris Kennaway DIR *dirp; 914b66f2d16SKris Kennaway struct dirent *dp; 915b66f2d16SKris Kennaway char *path; 916b66f2d16SKris Kennaway int handle; 917b66f2d16SKris Kennaway u_int32_t id; 918b66f2d16SKris Kennaway 919b66f2d16SKris Kennaway id = get_int(); 920b66f2d16SKris Kennaway handle = get_handle(); 921761efaa7SDag-Erling Smørgrav debug("request %u: readdir \"%s\" (handle %d)", id, 922761efaa7SDag-Erling Smørgrav handle_to_name(handle), handle); 923b66f2d16SKris Kennaway dirp = handle_to_dir(handle); 924b66f2d16SKris Kennaway path = handle_to_name(handle); 925b66f2d16SKris Kennaway if (dirp == NULL || path == NULL) { 9261e8db6e2SBrian Feldman send_status(id, SSH2_FX_FAILURE); 927b66f2d16SKris Kennaway } else { 928b66f2d16SKris Kennaway struct stat st; 929761efaa7SDag-Erling Smørgrav char pathname[MAXPATHLEN]; 930b66f2d16SKris Kennaway Stat *stats; 931b66f2d16SKris Kennaway int nstats = 10, count = 0, i; 932ee21a45fSDag-Erling Smørgrav 933761efaa7SDag-Erling Smørgrav stats = xcalloc(nstats, sizeof(Stat)); 934b66f2d16SKris Kennaway while ((dp = readdir(dirp)) != NULL) { 935b66f2d16SKris Kennaway if (count >= nstats) { 936b66f2d16SKris Kennaway nstats *= 2; 937761efaa7SDag-Erling Smørgrav stats = xrealloc(stats, nstats, sizeof(Stat)); 938b66f2d16SKris Kennaway } 939b66f2d16SKris Kennaway /* XXX OVERFLOW ? */ 940ae1f160dSDag-Erling Smørgrav snprintf(pathname, sizeof pathname, "%s%s%s", path, 941ae1f160dSDag-Erling Smørgrav strcmp(path, "/") ? "/" : "", dp->d_name); 942b66f2d16SKris Kennaway if (lstat(pathname, &st) < 0) 943b66f2d16SKris Kennaway continue; 9441e8db6e2SBrian Feldman stat_to_attrib(&st, &(stats[count].attrib)); 945b66f2d16SKris Kennaway stats[count].name = xstrdup(dp->d_name); 946b15c8340SDag-Erling Smørgrav stats[count].long_name = ls_file(dp->d_name, &st, 0, 0); 947b66f2d16SKris Kennaway count++; 948b66f2d16SKris Kennaway /* send up to 100 entries in one message */ 9491e8db6e2SBrian Feldman /* XXX check packet size instead */ 950b66f2d16SKris Kennaway if (count == 100) 951b66f2d16SKris Kennaway break; 952b66f2d16SKris Kennaway } 9531e8db6e2SBrian Feldman if (count > 0) { 954b66f2d16SKris Kennaway send_names(id, count, stats); 955b66f2d16SKris Kennaway for (i = 0; i < count; i++) { 956*e4a9863fSDag-Erling Smørgrav free(stats[i].name); 957*e4a9863fSDag-Erling Smørgrav free(stats[i].long_name); 958b66f2d16SKris Kennaway } 9591e8db6e2SBrian Feldman } else { 9601e8db6e2SBrian Feldman send_status(id, SSH2_FX_EOF); 9611e8db6e2SBrian Feldman } 962*e4a9863fSDag-Erling Smørgrav free(stats); 963b66f2d16SKris Kennaway } 964b66f2d16SKris Kennaway } 965b66f2d16SKris Kennaway 966ae1f160dSDag-Erling Smørgrav static void 967b66f2d16SKris Kennaway process_remove(void) 968b66f2d16SKris Kennaway { 969b66f2d16SKris Kennaway char *name; 970b66f2d16SKris Kennaway u_int32_t id; 9711e8db6e2SBrian Feldman int status = SSH2_FX_FAILURE; 972b66f2d16SKris Kennaway int ret; 973b66f2d16SKris Kennaway 974b66f2d16SKris Kennaway id = get_int(); 975b66f2d16SKris Kennaway name = get_string(NULL); 976761efaa7SDag-Erling Smørgrav debug3("request %u: remove", id); 977761efaa7SDag-Erling Smørgrav logit("remove name \"%s\"", name); 978b15c8340SDag-Erling Smørgrav if (readonly) 979b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 980b15c8340SDag-Erling Smørgrav else { 9811e8db6e2SBrian Feldman ret = unlink(name); 9821e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 983b15c8340SDag-Erling Smørgrav } 984b66f2d16SKris Kennaway send_status(id, status); 985*e4a9863fSDag-Erling Smørgrav free(name); 986b66f2d16SKris Kennaway } 987b66f2d16SKris Kennaway 988ae1f160dSDag-Erling Smørgrav static void 989b66f2d16SKris Kennaway process_mkdir(void) 990b66f2d16SKris Kennaway { 991b66f2d16SKris Kennaway Attrib *a; 992b66f2d16SKris Kennaway u_int32_t id; 993b66f2d16SKris Kennaway char *name; 9941e8db6e2SBrian Feldman int ret, mode, status = SSH2_FX_FAILURE; 995b66f2d16SKris Kennaway 996b66f2d16SKris Kennaway id = get_int(); 997b66f2d16SKris Kennaway name = get_string(NULL); 998b66f2d16SKris Kennaway a = get_attrib(); 9991e8db6e2SBrian Feldman mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 1000d4af9e69SDag-Erling Smørgrav a->perm & 07777 : 0777; 1001761efaa7SDag-Erling Smørgrav debug3("request %u: mkdir", id); 1002761efaa7SDag-Erling Smørgrav logit("mkdir name \"%s\" mode 0%o", name, mode); 1003b15c8340SDag-Erling Smørgrav if (readonly) 1004b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 1005b15c8340SDag-Erling Smørgrav else { 1006b66f2d16SKris Kennaway ret = mkdir(name, mode); 10071e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1008b15c8340SDag-Erling Smørgrav } 1009b66f2d16SKris Kennaway send_status(id, status); 1010*e4a9863fSDag-Erling Smørgrav free(name); 1011b66f2d16SKris Kennaway } 1012b66f2d16SKris Kennaway 1013ae1f160dSDag-Erling Smørgrav static void 1014b66f2d16SKris Kennaway process_rmdir(void) 1015b66f2d16SKris Kennaway { 1016b66f2d16SKris Kennaway u_int32_t id; 1017b66f2d16SKris Kennaway char *name; 1018b66f2d16SKris Kennaway int ret, status; 1019b66f2d16SKris Kennaway 1020b66f2d16SKris Kennaway id = get_int(); 1021b66f2d16SKris Kennaway name = get_string(NULL); 1022761efaa7SDag-Erling Smørgrav debug3("request %u: rmdir", id); 1023761efaa7SDag-Erling Smørgrav logit("rmdir name \"%s\"", name); 1024b15c8340SDag-Erling Smørgrav if (readonly) 1025b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 1026b15c8340SDag-Erling Smørgrav else { 1027b66f2d16SKris Kennaway ret = rmdir(name); 10281e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1029b15c8340SDag-Erling Smørgrav } 1030b66f2d16SKris Kennaway send_status(id, status); 1031*e4a9863fSDag-Erling Smørgrav free(name); 1032b66f2d16SKris Kennaway } 1033b66f2d16SKris Kennaway 1034ae1f160dSDag-Erling Smørgrav static void 1035b66f2d16SKris Kennaway process_realpath(void) 1036b66f2d16SKris Kennaway { 1037b66f2d16SKris Kennaway char resolvedname[MAXPATHLEN]; 1038b66f2d16SKris Kennaway u_int32_t id; 1039b66f2d16SKris Kennaway char *path; 1040b66f2d16SKris Kennaway 1041b66f2d16SKris Kennaway id = get_int(); 1042b66f2d16SKris Kennaway path = get_string(NULL); 10431e8db6e2SBrian Feldman if (path[0] == '\0') { 1044*e4a9863fSDag-Erling Smørgrav free(path); 10451e8db6e2SBrian Feldman path = xstrdup("."); 10461e8db6e2SBrian Feldman } 1047761efaa7SDag-Erling Smørgrav debug3("request %u: realpath", id); 1048761efaa7SDag-Erling Smørgrav verbose("realpath \"%s\"", path); 1049b66f2d16SKris Kennaway if (realpath(path, resolvedname) == NULL) { 1050b66f2d16SKris Kennaway send_status(id, errno_to_portable(errno)); 1051b66f2d16SKris Kennaway } else { 1052b66f2d16SKris Kennaway Stat s; 1053b66f2d16SKris Kennaway attrib_clear(&s.attrib); 1054b66f2d16SKris Kennaway s.name = s.long_name = resolvedname; 1055b66f2d16SKris Kennaway send_names(id, 1, &s); 1056b66f2d16SKris Kennaway } 1057*e4a9863fSDag-Erling Smørgrav free(path); 1058b66f2d16SKris Kennaway } 1059b66f2d16SKris Kennaway 1060ae1f160dSDag-Erling Smørgrav static void 1061b66f2d16SKris Kennaway process_rename(void) 1062b66f2d16SKris Kennaway { 1063b66f2d16SKris Kennaway u_int32_t id; 1064b66f2d16SKris Kennaway char *oldpath, *newpath; 1065d0c8c0bcSDag-Erling Smørgrav int status; 1066d0c8c0bcSDag-Erling Smørgrav struct stat sb; 1067b66f2d16SKris Kennaway 1068b66f2d16SKris Kennaway id = get_int(); 1069b66f2d16SKris Kennaway oldpath = get_string(NULL); 1070b66f2d16SKris Kennaway newpath = get_string(NULL); 1071761efaa7SDag-Erling Smørgrav debug3("request %u: rename", id); 1072761efaa7SDag-Erling Smørgrav logit("rename old \"%s\" new \"%s\"", oldpath, newpath); 1073d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_FAILURE; 1074b15c8340SDag-Erling Smørgrav if (readonly) 1075b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 1076b15c8340SDag-Erling Smørgrav else if (lstat(oldpath, &sb) == -1) 1077d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1078d0c8c0bcSDag-Erling Smørgrav else if (S_ISREG(sb.st_mode)) { 1079d0c8c0bcSDag-Erling Smørgrav /* Race-free rename of regular files */ 1080d74d50a8SDag-Erling Smørgrav if (link(oldpath, newpath) == -1) { 10817aee6ffeSDag-Erling Smørgrav if (errno == EOPNOTSUPP || errno == ENOSYS 1082d4af9e69SDag-Erling Smørgrav #ifdef EXDEV 1083d4af9e69SDag-Erling Smørgrav || errno == EXDEV 1084d4af9e69SDag-Erling Smørgrav #endif 1085d74d50a8SDag-Erling Smørgrav #ifdef LINK_OPNOTSUPP_ERRNO 1086d74d50a8SDag-Erling Smørgrav || errno == LINK_OPNOTSUPP_ERRNO 1087d74d50a8SDag-Erling Smørgrav #endif 1088d74d50a8SDag-Erling Smørgrav ) { 1089d74d50a8SDag-Erling Smørgrav struct stat st; 1090d74d50a8SDag-Erling Smørgrav 1091d74d50a8SDag-Erling Smørgrav /* 1092d74d50a8SDag-Erling Smørgrav * fs doesn't support links, so fall back to 1093d74d50a8SDag-Erling Smørgrav * stat+rename. This is racy. 1094d74d50a8SDag-Erling Smørgrav */ 1095d74d50a8SDag-Erling Smørgrav if (stat(newpath, &st) == -1) { 1096d74d50a8SDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1097d74d50a8SDag-Erling Smørgrav status = 1098d74d50a8SDag-Erling Smørgrav errno_to_portable(errno); 1099d74d50a8SDag-Erling Smørgrav else 1100d74d50a8SDag-Erling Smørgrav status = SSH2_FX_OK; 1101d74d50a8SDag-Erling Smørgrav } 1102d74d50a8SDag-Erling Smørgrav } else { 1103d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1104d74d50a8SDag-Erling Smørgrav } 1105d74d50a8SDag-Erling Smørgrav } else if (unlink(oldpath) == -1) { 1106d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1107d0c8c0bcSDag-Erling Smørgrav /* clean spare link */ 1108d0c8c0bcSDag-Erling Smørgrav unlink(newpath); 1109d0c8c0bcSDag-Erling Smørgrav } else 1110d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 1111d0c8c0bcSDag-Erling Smørgrav } else if (stat(newpath, &sb) == -1) { 1112d0c8c0bcSDag-Erling Smørgrav if (rename(oldpath, newpath) == -1) 1113d0c8c0bcSDag-Erling Smørgrav status = errno_to_portable(errno); 1114d0c8c0bcSDag-Erling Smørgrav else 1115d0c8c0bcSDag-Erling Smørgrav status = SSH2_FX_OK; 11161e8db6e2SBrian Feldman } 1117b66f2d16SKris Kennaway send_status(id, status); 1118*e4a9863fSDag-Erling Smørgrav free(oldpath); 1119*e4a9863fSDag-Erling Smørgrav free(newpath); 1120b66f2d16SKris Kennaway } 1121b66f2d16SKris Kennaway 1122ae1f160dSDag-Erling Smørgrav static void 11231e8db6e2SBrian Feldman process_readlink(void) 11241e8db6e2SBrian Feldman { 11251e8db6e2SBrian Feldman u_int32_t id; 1126ae1f160dSDag-Erling Smørgrav int len; 1127d74d50a8SDag-Erling Smørgrav char buf[MAXPATHLEN]; 11281e8db6e2SBrian Feldman char *path; 11291e8db6e2SBrian Feldman 11301e8db6e2SBrian Feldman id = get_int(); 11311e8db6e2SBrian Feldman path = get_string(NULL); 1132761efaa7SDag-Erling Smørgrav debug3("request %u: readlink", id); 1133761efaa7SDag-Erling Smørgrav verbose("readlink \"%s\"", path); 1134d74d50a8SDag-Erling Smørgrav if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) 11351e8db6e2SBrian Feldman send_status(id, errno_to_portable(errno)); 11361e8db6e2SBrian Feldman else { 11371e8db6e2SBrian Feldman Stat s; 11381e8db6e2SBrian Feldman 1139d74d50a8SDag-Erling Smørgrav buf[len] = '\0'; 11401e8db6e2SBrian Feldman attrib_clear(&s.attrib); 1141d74d50a8SDag-Erling Smørgrav s.name = s.long_name = buf; 11421e8db6e2SBrian Feldman send_names(id, 1, &s); 11431e8db6e2SBrian Feldman } 1144*e4a9863fSDag-Erling Smørgrav free(path); 11451e8db6e2SBrian Feldman } 11461e8db6e2SBrian Feldman 1147ae1f160dSDag-Erling Smørgrav static void 11481e8db6e2SBrian Feldman process_symlink(void) 11491e8db6e2SBrian Feldman { 11501e8db6e2SBrian Feldman u_int32_t id; 11511e8db6e2SBrian Feldman char *oldpath, *newpath; 1152d0c8c0bcSDag-Erling Smørgrav int ret, status; 11531e8db6e2SBrian Feldman 11541e8db6e2SBrian Feldman id = get_int(); 11551e8db6e2SBrian Feldman oldpath = get_string(NULL); 11561e8db6e2SBrian Feldman newpath = get_string(NULL); 1157761efaa7SDag-Erling Smørgrav debug3("request %u: symlink", id); 1158761efaa7SDag-Erling Smørgrav logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); 1159d0c8c0bcSDag-Erling Smørgrav /* this will fail if 'newpath' exists */ 1160b15c8340SDag-Erling Smørgrav if (readonly) 1161b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 1162b15c8340SDag-Erling Smørgrav else { 11631e8db6e2SBrian Feldman ret = symlink(oldpath, newpath); 11641e8db6e2SBrian Feldman status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1165b15c8340SDag-Erling Smørgrav } 11661e8db6e2SBrian Feldman send_status(id, status); 1167*e4a9863fSDag-Erling Smørgrav free(oldpath); 1168*e4a9863fSDag-Erling Smørgrav free(newpath); 11691e8db6e2SBrian Feldman } 11701e8db6e2SBrian Feldman 1171ae1f160dSDag-Erling Smørgrav static void 1172d4af9e69SDag-Erling Smørgrav process_extended_posix_rename(u_int32_t id) 1173d4af9e69SDag-Erling Smørgrav { 1174d4af9e69SDag-Erling Smørgrav char *oldpath, *newpath; 1175b15c8340SDag-Erling Smørgrav int ret, status; 1176d4af9e69SDag-Erling Smørgrav 1177d4af9e69SDag-Erling Smørgrav oldpath = get_string(NULL); 1178d4af9e69SDag-Erling Smørgrav newpath = get_string(NULL); 1179d4af9e69SDag-Erling Smørgrav debug3("request %u: posix-rename", id); 1180d4af9e69SDag-Erling Smørgrav logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); 1181b15c8340SDag-Erling Smørgrav if (readonly) 1182b15c8340SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 1183b15c8340SDag-Erling Smørgrav else { 1184b15c8340SDag-Erling Smørgrav ret = rename(oldpath, newpath); 1185b15c8340SDag-Erling Smørgrav status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1186b15c8340SDag-Erling Smørgrav } 1187b15c8340SDag-Erling Smørgrav send_status(id, status); 1188*e4a9863fSDag-Erling Smørgrav free(oldpath); 1189*e4a9863fSDag-Erling Smørgrav free(newpath); 1190d4af9e69SDag-Erling Smørgrav } 1191d4af9e69SDag-Erling Smørgrav 1192d4af9e69SDag-Erling Smørgrav static void 1193d4af9e69SDag-Erling Smørgrav process_extended_statvfs(u_int32_t id) 1194d4af9e69SDag-Erling Smørgrav { 1195d4af9e69SDag-Erling Smørgrav char *path; 1196d4af9e69SDag-Erling Smørgrav struct statvfs st; 1197d4af9e69SDag-Erling Smørgrav 1198d4af9e69SDag-Erling Smørgrav path = get_string(NULL); 1199d4af9e69SDag-Erling Smørgrav debug3("request %u: statfs", id); 1200d4af9e69SDag-Erling Smørgrav logit("statfs \"%s\"", path); 1201d4af9e69SDag-Erling Smørgrav 1202d4af9e69SDag-Erling Smørgrav if (statvfs(path, &st) != 0) 1203d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1204d4af9e69SDag-Erling Smørgrav else 1205d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st); 1206*e4a9863fSDag-Erling Smørgrav free(path); 1207d4af9e69SDag-Erling Smørgrav } 1208d4af9e69SDag-Erling Smørgrav 1209d4af9e69SDag-Erling Smørgrav static void 1210d4af9e69SDag-Erling Smørgrav process_extended_fstatvfs(u_int32_t id) 1211d4af9e69SDag-Erling Smørgrav { 1212d4af9e69SDag-Erling Smørgrav int handle, fd; 1213d4af9e69SDag-Erling Smørgrav struct statvfs st; 1214d4af9e69SDag-Erling Smørgrav 1215d4af9e69SDag-Erling Smørgrav handle = get_handle(); 1216d4af9e69SDag-Erling Smørgrav debug("request %u: fstatvfs \"%s\" (handle %u)", 1217d4af9e69SDag-Erling Smørgrav id, handle_to_name(handle), handle); 1218d4af9e69SDag-Erling Smørgrav if ((fd = handle_to_fd(handle)) < 0) { 1219d4af9e69SDag-Erling Smørgrav send_status(id, SSH2_FX_FAILURE); 1220d4af9e69SDag-Erling Smørgrav return; 1221d4af9e69SDag-Erling Smørgrav } 1222d4af9e69SDag-Erling Smørgrav if (fstatvfs(fd, &st) != 0) 1223d4af9e69SDag-Erling Smørgrav send_status(id, errno_to_portable(errno)); 1224d4af9e69SDag-Erling Smørgrav else 1225d4af9e69SDag-Erling Smørgrav send_statvfs(id, &st); 1226d4af9e69SDag-Erling Smørgrav } 1227d4af9e69SDag-Erling Smørgrav 1228d4af9e69SDag-Erling Smørgrav static void 12294a421b63SDag-Erling Smørgrav process_extended_hardlink(u_int32_t id) 12304a421b63SDag-Erling Smørgrav { 12314a421b63SDag-Erling Smørgrav char *oldpath, *newpath; 12324a421b63SDag-Erling Smørgrav int ret, status; 12334a421b63SDag-Erling Smørgrav 12344a421b63SDag-Erling Smørgrav oldpath = get_string(NULL); 12354a421b63SDag-Erling Smørgrav newpath = get_string(NULL); 12364a421b63SDag-Erling Smørgrav debug3("request %u: hardlink", id); 12374a421b63SDag-Erling Smørgrav logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath); 12384a421b63SDag-Erling Smørgrav if (readonly) 12394a421b63SDag-Erling Smørgrav status = SSH2_FX_PERMISSION_DENIED; 12404a421b63SDag-Erling Smørgrav else { 12414a421b63SDag-Erling Smørgrav ret = link(oldpath, newpath); 12424a421b63SDag-Erling Smørgrav status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 12434a421b63SDag-Erling Smørgrav } 12444a421b63SDag-Erling Smørgrav send_status(id, status); 1245*e4a9863fSDag-Erling Smørgrav free(oldpath); 1246*e4a9863fSDag-Erling Smørgrav free(newpath); 12474a421b63SDag-Erling Smørgrav } 12484a421b63SDag-Erling Smørgrav 12494a421b63SDag-Erling Smørgrav static void 12501e8db6e2SBrian Feldman process_extended(void) 12511e8db6e2SBrian Feldman { 12521e8db6e2SBrian Feldman u_int32_t id; 12531e8db6e2SBrian Feldman char *request; 12541e8db6e2SBrian Feldman 12551e8db6e2SBrian Feldman id = get_int(); 12561e8db6e2SBrian Feldman request = get_string(NULL); 1257d4af9e69SDag-Erling Smørgrav if (strcmp(request, "posix-rename@openssh.com") == 0) 1258d4af9e69SDag-Erling Smørgrav process_extended_posix_rename(id); 1259d4af9e69SDag-Erling Smørgrav else if (strcmp(request, "statvfs@openssh.com") == 0) 1260d4af9e69SDag-Erling Smørgrav process_extended_statvfs(id); 1261d4af9e69SDag-Erling Smørgrav else if (strcmp(request, "fstatvfs@openssh.com") == 0) 1262d4af9e69SDag-Erling Smørgrav process_extended_fstatvfs(id); 12634a421b63SDag-Erling Smørgrav else if (strcmp(request, "hardlink@openssh.com") == 0) 12644a421b63SDag-Erling Smørgrav process_extended_hardlink(id); 1265d4af9e69SDag-Erling Smørgrav else 12661e8db6e2SBrian Feldman send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 1267*e4a9863fSDag-Erling Smørgrav free(request); 12681e8db6e2SBrian Feldman } 1269b66f2d16SKris Kennaway 1270b66f2d16SKris Kennaway /* stolen from ssh-agent */ 1271b66f2d16SKris Kennaway 1272ae1f160dSDag-Erling Smørgrav static void 1273b66f2d16SKris Kennaway process(void) 1274b66f2d16SKris Kennaway { 12751e8db6e2SBrian Feldman u_int msg_len; 1276545d5ecaSDag-Erling Smørgrav u_int buf_len; 1277545d5ecaSDag-Erling Smørgrav u_int consumed; 12781e8db6e2SBrian Feldman u_int type; 12791e8db6e2SBrian Feldman u_char *cp; 1280b66f2d16SKris Kennaway 1281545d5ecaSDag-Erling Smørgrav buf_len = buffer_len(&iqueue); 1282545d5ecaSDag-Erling Smørgrav if (buf_len < 5) 1283b66f2d16SKris Kennaway return; /* Incomplete message. */ 1284ae1f160dSDag-Erling Smørgrav cp = buffer_ptr(&iqueue); 1285761efaa7SDag-Erling Smørgrav msg_len = get_u32(cp); 1286021d409fSDag-Erling Smørgrav if (msg_len > SFTP_MAX_MSG_LENGTH) { 1287761efaa7SDag-Erling Smørgrav error("bad message from %s local user %s", 1288761efaa7SDag-Erling Smørgrav client_addr, pw->pw_name); 1289d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(11); 1290b66f2d16SKris Kennaway } 1291545d5ecaSDag-Erling Smørgrav if (buf_len < msg_len + 4) 1292b66f2d16SKris Kennaway return; 1293b66f2d16SKris Kennaway buffer_consume(&iqueue, 4); 1294545d5ecaSDag-Erling Smørgrav buf_len -= 4; 1295b66f2d16SKris Kennaway type = buffer_get_char(&iqueue); 1296b66f2d16SKris Kennaway switch (type) { 12971e8db6e2SBrian Feldman case SSH2_FXP_INIT: 1298b66f2d16SKris Kennaway process_init(); 1299b66f2d16SKris Kennaway break; 13001e8db6e2SBrian Feldman case SSH2_FXP_OPEN: 1301b66f2d16SKris Kennaway process_open(); 1302b66f2d16SKris Kennaway break; 13031e8db6e2SBrian Feldman case SSH2_FXP_CLOSE: 1304b66f2d16SKris Kennaway process_close(); 1305b66f2d16SKris Kennaway break; 13061e8db6e2SBrian Feldman case SSH2_FXP_READ: 1307b66f2d16SKris Kennaway process_read(); 1308b66f2d16SKris Kennaway break; 13091e8db6e2SBrian Feldman case SSH2_FXP_WRITE: 1310b66f2d16SKris Kennaway process_write(); 1311b66f2d16SKris Kennaway break; 13121e8db6e2SBrian Feldman case SSH2_FXP_LSTAT: 1313b66f2d16SKris Kennaway process_lstat(); 1314b66f2d16SKris Kennaway break; 13151e8db6e2SBrian Feldman case SSH2_FXP_FSTAT: 1316b66f2d16SKris Kennaway process_fstat(); 1317b66f2d16SKris Kennaway break; 13181e8db6e2SBrian Feldman case SSH2_FXP_SETSTAT: 1319b66f2d16SKris Kennaway process_setstat(); 1320b66f2d16SKris Kennaway break; 13211e8db6e2SBrian Feldman case SSH2_FXP_FSETSTAT: 1322b66f2d16SKris Kennaway process_fsetstat(); 1323b66f2d16SKris Kennaway break; 13241e8db6e2SBrian Feldman case SSH2_FXP_OPENDIR: 1325b66f2d16SKris Kennaway process_opendir(); 1326b66f2d16SKris Kennaway break; 13271e8db6e2SBrian Feldman case SSH2_FXP_READDIR: 1328b66f2d16SKris Kennaway process_readdir(); 1329b66f2d16SKris Kennaway break; 13301e8db6e2SBrian Feldman case SSH2_FXP_REMOVE: 1331b66f2d16SKris Kennaway process_remove(); 1332b66f2d16SKris Kennaway break; 13331e8db6e2SBrian Feldman case SSH2_FXP_MKDIR: 1334b66f2d16SKris Kennaway process_mkdir(); 1335b66f2d16SKris Kennaway break; 13361e8db6e2SBrian Feldman case SSH2_FXP_RMDIR: 1337b66f2d16SKris Kennaway process_rmdir(); 1338b66f2d16SKris Kennaway break; 13391e8db6e2SBrian Feldman case SSH2_FXP_REALPATH: 1340b66f2d16SKris Kennaway process_realpath(); 1341b66f2d16SKris Kennaway break; 13421e8db6e2SBrian Feldman case SSH2_FXP_STAT: 1343b66f2d16SKris Kennaway process_stat(); 1344b66f2d16SKris Kennaway break; 13451e8db6e2SBrian Feldman case SSH2_FXP_RENAME: 1346b66f2d16SKris Kennaway process_rename(); 1347b66f2d16SKris Kennaway break; 13481e8db6e2SBrian Feldman case SSH2_FXP_READLINK: 13491e8db6e2SBrian Feldman process_readlink(); 13501e8db6e2SBrian Feldman break; 13511e8db6e2SBrian Feldman case SSH2_FXP_SYMLINK: 13521e8db6e2SBrian Feldman process_symlink(); 13531e8db6e2SBrian Feldman break; 13541e8db6e2SBrian Feldman case SSH2_FXP_EXTENDED: 13551e8db6e2SBrian Feldman process_extended(); 13561e8db6e2SBrian Feldman break; 1357b66f2d16SKris Kennaway default: 1358b66f2d16SKris Kennaway error("Unknown message %d", type); 1359b66f2d16SKris Kennaway break; 1360b66f2d16SKris Kennaway } 1361545d5ecaSDag-Erling Smørgrav /* discard the remaining bytes from the current packet */ 1362d4af9e69SDag-Erling Smørgrav if (buf_len < buffer_len(&iqueue)) { 1363d4af9e69SDag-Erling Smørgrav error("iqueue grew unexpectedly"); 1364d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1365d4af9e69SDag-Erling Smørgrav } 1366545d5ecaSDag-Erling Smørgrav consumed = buf_len - buffer_len(&iqueue); 1367d4af9e69SDag-Erling Smørgrav if (msg_len < consumed) { 1368d4af9e69SDag-Erling Smørgrav error("msg_len %d < consumed %d", msg_len, consumed); 1369d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1370d4af9e69SDag-Erling Smørgrav } 1371545d5ecaSDag-Erling Smørgrav if (msg_len > consumed) 1372545d5ecaSDag-Erling Smørgrav buffer_consume(&iqueue, msg_len - consumed); 1373b66f2d16SKris Kennaway } 1374b66f2d16SKris Kennaway 1375761efaa7SDag-Erling Smørgrav /* Cleanup handler that logs active handles upon normal exit */ 1376761efaa7SDag-Erling Smørgrav void 1377d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(int i) 1378761efaa7SDag-Erling Smørgrav { 1379761efaa7SDag-Erling Smørgrav if (pw != NULL && client_addr != NULL) { 1380761efaa7SDag-Erling Smørgrav handle_log_exit(); 1381761efaa7SDag-Erling Smørgrav logit("session closed for local user %s from [%s]", 1382761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr); 1383761efaa7SDag-Erling Smørgrav } 1384761efaa7SDag-Erling Smørgrav _exit(i); 1385761efaa7SDag-Erling Smørgrav } 1386761efaa7SDag-Erling Smørgrav 1387761efaa7SDag-Erling Smørgrav static void 1388d4af9e69SDag-Erling Smørgrav sftp_server_usage(void) 1389761efaa7SDag-Erling Smørgrav { 1390761efaa7SDag-Erling Smørgrav extern char *__progname; 1391761efaa7SDag-Erling Smørgrav 1392761efaa7SDag-Erling Smørgrav fprintf(stderr, 13936888a9beSDag-Erling Smørgrav "usage: %s [-ehR] [-d start_directory] [-f log_facility] " 13946888a9beSDag-Erling Smørgrav "[-l log_level]\n\t[-u umask]\n", 1395b15c8340SDag-Erling Smørgrav __progname); 1396761efaa7SDag-Erling Smørgrav exit(1); 1397761efaa7SDag-Erling Smørgrav } 1398761efaa7SDag-Erling Smørgrav 1399b66f2d16SKris Kennaway int 1400d4af9e69SDag-Erling Smørgrav sftp_server_main(int argc, char **argv, struct passwd *user_pw) 1401b66f2d16SKris Kennaway { 14021e8db6e2SBrian Feldman fd_set *rset, *wset; 1403761efaa7SDag-Erling Smørgrav int in, out, max, ch, skipargs = 0, log_stderr = 0; 14041e8db6e2SBrian Feldman ssize_t len, olen, set_size; 1405761efaa7SDag-Erling Smørgrav SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; 14066888a9beSDag-Erling Smørgrav char *cp, *homedir = NULL, buf[4*4096]; 14074a421b63SDag-Erling Smørgrav long mask; 1408761efaa7SDag-Erling Smørgrav 1409761efaa7SDag-Erling Smørgrav extern char *optarg; 1410761efaa7SDag-Erling Smørgrav extern char *__progname; 14111e8db6e2SBrian Feldman 1412761efaa7SDag-Erling Smørgrav __progname = ssh_get_progname(argv[0]); 1413761efaa7SDag-Erling Smørgrav log_init(__progname, log_level, log_facility, log_stderr); 1414b66f2d16SKris Kennaway 14156888a9beSDag-Erling Smørgrav pw = pwcopy(user_pw); 14166888a9beSDag-Erling Smørgrav 14176888a9beSDag-Erling Smørgrav while (!skipargs && (ch = getopt(argc, argv, "d:f:l:u:cehR")) != -1) { 1418761efaa7SDag-Erling Smørgrav switch (ch) { 1419b15c8340SDag-Erling Smørgrav case 'R': 1420b15c8340SDag-Erling Smørgrav readonly = 1; 1421b15c8340SDag-Erling Smørgrav break; 1422761efaa7SDag-Erling Smørgrav case 'c': 1423761efaa7SDag-Erling Smørgrav /* 1424761efaa7SDag-Erling Smørgrav * Ignore all arguments if we are invoked as a 1425761efaa7SDag-Erling Smørgrav * shell using "sftp-server -c command" 1426761efaa7SDag-Erling Smørgrav */ 1427761efaa7SDag-Erling Smørgrav skipargs = 1; 1428761efaa7SDag-Erling Smørgrav break; 1429761efaa7SDag-Erling Smørgrav case 'e': 1430761efaa7SDag-Erling Smørgrav log_stderr = 1; 1431761efaa7SDag-Erling Smørgrav break; 1432761efaa7SDag-Erling Smørgrav case 'l': 1433761efaa7SDag-Erling Smørgrav log_level = log_level_number(optarg); 1434761efaa7SDag-Erling Smørgrav if (log_level == SYSLOG_LEVEL_NOT_SET) 1435761efaa7SDag-Erling Smørgrav error("Invalid log level \"%s\"", optarg); 1436761efaa7SDag-Erling Smørgrav break; 1437761efaa7SDag-Erling Smørgrav case 'f': 1438761efaa7SDag-Erling Smørgrav log_facility = log_facility_number(optarg); 1439d4af9e69SDag-Erling Smørgrav if (log_facility == SYSLOG_FACILITY_NOT_SET) 1440761efaa7SDag-Erling Smørgrav error("Invalid log facility \"%s\"", optarg); 1441761efaa7SDag-Erling Smørgrav break; 14426888a9beSDag-Erling Smørgrav case 'd': 14436888a9beSDag-Erling Smørgrav cp = tilde_expand_filename(optarg, user_pw->pw_uid); 14446888a9beSDag-Erling Smørgrav homedir = percent_expand(cp, "d", user_pw->pw_dir, 14456888a9beSDag-Erling Smørgrav "u", user_pw->pw_name, (char *)NULL); 14466888a9beSDag-Erling Smørgrav free(cp); 14476888a9beSDag-Erling Smørgrav break; 1448b15c8340SDag-Erling Smørgrav case 'u': 14494a421b63SDag-Erling Smørgrav errno = 0; 14504a421b63SDag-Erling Smørgrav mask = strtol(optarg, &cp, 8); 14514a421b63SDag-Erling Smørgrav if (mask < 0 || mask > 0777 || *cp != '\0' || 14524a421b63SDag-Erling Smørgrav cp == optarg || (mask == 0 && errno != 0)) 14534a421b63SDag-Erling Smørgrav fatal("Invalid umask \"%s\"", optarg); 14544a421b63SDag-Erling Smørgrav (void)umask((mode_t)mask); 1455b15c8340SDag-Erling Smørgrav break; 1456761efaa7SDag-Erling Smørgrav case 'h': 1457761efaa7SDag-Erling Smørgrav default: 1458d4af9e69SDag-Erling Smørgrav sftp_server_usage(); 1459761efaa7SDag-Erling Smørgrav } 1460761efaa7SDag-Erling Smørgrav } 1461761efaa7SDag-Erling Smørgrav 1462761efaa7SDag-Erling Smørgrav log_init(__progname, log_level, log_facility, log_stderr); 1463761efaa7SDag-Erling Smørgrav 1464761efaa7SDag-Erling Smørgrav if ((cp = getenv("SSH_CONNECTION")) != NULL) { 1465761efaa7SDag-Erling Smørgrav client_addr = xstrdup(cp); 1466d4af9e69SDag-Erling Smørgrav if ((cp = strchr(client_addr, ' ')) == NULL) { 1467d4af9e69SDag-Erling Smørgrav error("Malformed SSH_CONNECTION variable: \"%s\"", 1468761efaa7SDag-Erling Smørgrav getenv("SSH_CONNECTION")); 1469d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(255); 1470d4af9e69SDag-Erling Smørgrav } 1471761efaa7SDag-Erling Smørgrav *cp = '\0'; 1472761efaa7SDag-Erling Smørgrav } else 1473761efaa7SDag-Erling Smørgrav client_addr = xstrdup("UNKNOWN"); 1474761efaa7SDag-Erling Smørgrav 1475761efaa7SDag-Erling Smørgrav logit("session opened for local user %s from [%s]", 1476761efaa7SDag-Erling Smørgrav pw->pw_name, client_addr); 1477761efaa7SDag-Erling Smørgrav 1478b15c8340SDag-Erling Smørgrav in = STDIN_FILENO; 1479b15c8340SDag-Erling Smørgrav out = STDOUT_FILENO; 1480b66f2d16SKris Kennaway 148183d2307dSDag-Erling Smørgrav #ifdef HAVE_CYGWIN 148283d2307dSDag-Erling Smørgrav setmode(in, O_BINARY); 148383d2307dSDag-Erling Smørgrav setmode(out, O_BINARY); 148483d2307dSDag-Erling Smørgrav #endif 148583d2307dSDag-Erling Smørgrav 1486b66f2d16SKris Kennaway max = 0; 1487b66f2d16SKris Kennaway if (in > max) 1488b66f2d16SKris Kennaway max = in; 1489b66f2d16SKris Kennaway if (out > max) 1490b66f2d16SKris Kennaway max = out; 1491b66f2d16SKris Kennaway 1492b66f2d16SKris Kennaway buffer_init(&iqueue); 1493b66f2d16SKris Kennaway buffer_init(&oqueue); 1494b66f2d16SKris Kennaway 14951e8db6e2SBrian Feldman set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 14961e8db6e2SBrian Feldman rset = (fd_set *)xmalloc(set_size); 14971e8db6e2SBrian Feldman wset = (fd_set *)xmalloc(set_size); 1498b66f2d16SKris Kennaway 14996888a9beSDag-Erling Smørgrav if (homedir != NULL) { 15006888a9beSDag-Erling Smørgrav if (chdir(homedir) != 0) { 15016888a9beSDag-Erling Smørgrav error("chdir to \"%s\" failed: %s", homedir, 15026888a9beSDag-Erling Smørgrav strerror(errno)); 15036888a9beSDag-Erling Smørgrav } 15046888a9beSDag-Erling Smørgrav } 15056888a9beSDag-Erling Smørgrav 15061e8db6e2SBrian Feldman for (;;) { 15071e8db6e2SBrian Feldman memset(rset, 0, set_size); 15081e8db6e2SBrian Feldman memset(wset, 0, set_size); 15091e8db6e2SBrian Feldman 1510d4af9e69SDag-Erling Smørgrav /* 1511d4af9e69SDag-Erling Smørgrav * Ensure that we can read a full buffer and handle 1512d4af9e69SDag-Erling Smørgrav * the worst-case length packet it can generate, 1513d4af9e69SDag-Erling Smørgrav * otherwise apply backpressure by stopping reads. 1514d4af9e69SDag-Erling Smørgrav */ 1515d4af9e69SDag-Erling Smørgrav if (buffer_check_alloc(&iqueue, sizeof(buf)) && 1516d4af9e69SDag-Erling Smørgrav buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) 15171e8db6e2SBrian Feldman FD_SET(in, rset); 1518d4af9e69SDag-Erling Smørgrav 1519b66f2d16SKris Kennaway olen = buffer_len(&oqueue); 1520b66f2d16SKris Kennaway if (olen > 0) 15211e8db6e2SBrian Feldman FD_SET(out, wset); 1522b66f2d16SKris Kennaway 15231e8db6e2SBrian Feldman if (select(max+1, rset, wset, NULL, NULL) < 0) { 1524b66f2d16SKris Kennaway if (errno == EINTR) 1525b66f2d16SKris Kennaway continue; 1526761efaa7SDag-Erling Smørgrav error("select: %s", strerror(errno)); 1527d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(2); 1528b66f2d16SKris Kennaway } 1529b66f2d16SKris Kennaway 1530b66f2d16SKris Kennaway /* copy stdin to iqueue */ 15311e8db6e2SBrian Feldman if (FD_ISSET(in, rset)) { 1532b66f2d16SKris Kennaway len = read(in, buf, sizeof buf); 1533b66f2d16SKris Kennaway if (len == 0) { 1534b66f2d16SKris Kennaway debug("read eof"); 1535d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(0); 1536b66f2d16SKris Kennaway } else if (len < 0) { 1537761efaa7SDag-Erling Smørgrav error("read: %s", strerror(errno)); 1538d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1); 1539b66f2d16SKris Kennaway } else { 1540b66f2d16SKris Kennaway buffer_append(&iqueue, buf, len); 1541b66f2d16SKris Kennaway } 1542b66f2d16SKris Kennaway } 1543b66f2d16SKris Kennaway /* send oqueue to stdout */ 15441e8db6e2SBrian Feldman if (FD_ISSET(out, wset)) { 1545b66f2d16SKris Kennaway len = write(out, buffer_ptr(&oqueue), olen); 1546b66f2d16SKris Kennaway if (len < 0) { 1547761efaa7SDag-Erling Smørgrav error("write: %s", strerror(errno)); 1548d4af9e69SDag-Erling Smørgrav sftp_server_cleanup_exit(1); 1549b66f2d16SKris Kennaway } else { 1550b66f2d16SKris Kennaway buffer_consume(&oqueue, len); 1551b66f2d16SKris Kennaway } 1552b66f2d16SKris Kennaway } 1553d4af9e69SDag-Erling Smørgrav 1554d4af9e69SDag-Erling Smørgrav /* 1555d4af9e69SDag-Erling Smørgrav * Process requests from client if we can fit the results 1556d4af9e69SDag-Erling Smørgrav * into the output buffer, otherwise stop processing input 1557d4af9e69SDag-Erling Smørgrav * and let the output queue drain. 1558d4af9e69SDag-Erling Smørgrav */ 1559d4af9e69SDag-Erling Smørgrav if (buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) 1560b66f2d16SKris Kennaway process(); 1561b66f2d16SKris Kennaway } 1562b66f2d16SKris Kennaway } 1563