sftp-server.c revision 197679
1197679Sdes/* $OpenBSD: sftp-server.c,v 1.85 2009/04/14 16:33:42 stevesk Exp $ */ 265668Skris/* 3126274Sdes * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. 465668Skris * 5126274Sdes * Permission to use, copy, modify, and distribute this software for any 6126274Sdes * purpose with or without fee is hereby granted, provided that the above 7126274Sdes * copyright notice and this permission notice appear in all copies. 865668Skris * 9126274Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10126274Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11126274Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12126274Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13126274Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14126274Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15126274Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1665668Skris */ 17162852Sdes 1865668Skris#include "includes.h" 1965668Skris 20162852Sdes#include <sys/types.h> 21162852Sdes#include <sys/param.h> 22162852Sdes#include <sys/stat.h> 23162852Sdes#ifdef HAVE_SYS_TIME_H 24162852Sdes# include <sys/time.h> 25162852Sdes#endif 26181111Sdes#ifdef HAVE_SYS_MOUNT_H 27181111Sdes#include <sys/mount.h> 28181111Sdes#endif 29181111Sdes#ifdef HAVE_SYS_STATVFS_H 30181111Sdes#include <sys/statvfs.h> 31181111Sdes#endif 32162852Sdes 33162852Sdes#include <dirent.h> 34162852Sdes#include <errno.h> 35162852Sdes#include <fcntl.h> 36162852Sdes#include <pwd.h> 37162852Sdes#include <stdlib.h> 38162852Sdes#include <stdio.h> 39162852Sdes#include <string.h> 40162852Sdes#include <pwd.h> 41162852Sdes#include <time.h> 42162852Sdes#include <unistd.h> 43162852Sdes#include <stdarg.h> 44162852Sdes 45162852Sdes#include "xmalloc.h" 4665668Skris#include "buffer.h" 4776259Sgreen#include "log.h" 48157016Sdes#include "misc.h" 49162852Sdes#include "uidswap.h" 5065668Skris 5176259Sgreen#include "sftp.h" 5276259Sgreen#include "sftp-common.h" 5365668Skris 5465668Skris/* helper */ 5576259Sgreen#define get_int64() buffer_get_int64(&iqueue); 5665668Skris#define get_int() buffer_get_int(&iqueue); 5765668Skris#define get_string(lenp) buffer_get_string(&iqueue, lenp); 5865668Skris 59162852Sdes/* Our verbosity */ 60162852SdesLogLevel log_level = SYSLOG_LEVEL_ERROR; 6198937Sdes 62162852Sdes/* Our client */ 63162852Sdesstruct passwd *pw = NULL; 64162852Sdeschar *client_addr = NULL; 65162852Sdes 6665668Skris/* input and output queue */ 6765668SkrisBuffer iqueue; 6865668SkrisBuffer oqueue; 6965668Skris 7076259Sgreen/* Version of client */ 7176259Sgreenint version; 7276259Sgreen 73124208Sdes/* portable attributes, etc. */ 7465668Skris 7565668Skristypedef struct Stat Stat; 7665668Skris 7776259Sgreenstruct Stat { 7865668Skris char *name; 7965668Skris char *long_name; 8065668Skris Attrib attrib; 8165668Skris}; 8265668Skris 8392555Sdesstatic int 8465668Skriserrno_to_portable(int unixerrno) 8565668Skris{ 8665668Skris int ret = 0; 8776259Sgreen 8865668Skris switch (unixerrno) { 8965668Skris case 0: 9076259Sgreen ret = SSH2_FX_OK; 9165668Skris break; 9265668Skris case ENOENT: 9365668Skris case ENOTDIR: 9465668Skris case EBADF: 9565668Skris case ELOOP: 9676259Sgreen ret = SSH2_FX_NO_SUCH_FILE; 9765668Skris break; 9865668Skris case EPERM: 9965668Skris case EACCES: 10065668Skris case EFAULT: 10176259Sgreen ret = SSH2_FX_PERMISSION_DENIED; 10265668Skris break; 10365668Skris case ENAMETOOLONG: 10465668Skris case EINVAL: 10576259Sgreen ret = SSH2_FX_BAD_MESSAGE; 10665668Skris break; 107181111Sdes case ENOSYS: 108181111Sdes ret = SSH2_FX_OP_UNSUPPORTED; 109181111Sdes break; 11065668Skris default: 11176259Sgreen ret = SSH2_FX_FAILURE; 11265668Skris break; 11365668Skris } 11465668Skris return ret; 11565668Skris} 11665668Skris 11792555Sdesstatic int 11865668Skrisflags_from_portable(int pflags) 11965668Skris{ 12065668Skris int flags = 0; 12176259Sgreen 12276259Sgreen if ((pflags & SSH2_FXF_READ) && 12376259Sgreen (pflags & SSH2_FXF_WRITE)) { 12465668Skris flags = O_RDWR; 12576259Sgreen } else if (pflags & SSH2_FXF_READ) { 12665668Skris flags = O_RDONLY; 12776259Sgreen } else if (pflags & SSH2_FXF_WRITE) { 12865668Skris flags = O_WRONLY; 12965668Skris } 13076259Sgreen if (pflags & SSH2_FXF_CREAT) 13165668Skris flags |= O_CREAT; 13276259Sgreen if (pflags & SSH2_FXF_TRUNC) 13365668Skris flags |= O_TRUNC; 13476259Sgreen if (pflags & SSH2_FXF_EXCL) 13565668Skris flags |= O_EXCL; 13665668Skris return flags; 13765668Skris} 13865668Skris 139162852Sdesstatic const char * 140162852Sdesstring_from_portable(int pflags) 141162852Sdes{ 142162852Sdes static char ret[128]; 143162852Sdes 144162852Sdes *ret = '\0'; 145162852Sdes 146162852Sdes#define PAPPEND(str) { \ 147162852Sdes if (*ret != '\0') \ 148162852Sdes strlcat(ret, ",", sizeof(ret)); \ 149162852Sdes strlcat(ret, str, sizeof(ret)); \ 150162852Sdes } 151162852Sdes 152162852Sdes if (pflags & SSH2_FXF_READ) 153162852Sdes PAPPEND("READ") 154162852Sdes if (pflags & SSH2_FXF_WRITE) 155162852Sdes PAPPEND("WRITE") 156162852Sdes if (pflags & SSH2_FXF_CREAT) 157162852Sdes PAPPEND("CREATE") 158162852Sdes if (pflags & SSH2_FXF_TRUNC) 159162852Sdes PAPPEND("TRUNCATE") 160162852Sdes if (pflags & SSH2_FXF_EXCL) 161162852Sdes PAPPEND("EXCL") 162162852Sdes 163162852Sdes return ret; 164162852Sdes} 165162852Sdes 16692555Sdesstatic Attrib * 16765668Skrisget_attrib(void) 16865668Skris{ 16965668Skris return decode_attrib(&iqueue); 17065668Skris} 17165668Skris 17265668Skris/* handle handles */ 17365668Skris 17465668Skristypedef struct Handle Handle; 17565668Skrisstruct Handle { 17665668Skris int use; 17765668Skris DIR *dirp; 17865668Skris int fd; 17965668Skris char *name; 180162852Sdes u_int64_t bytes_read, bytes_write; 181181111Sdes int next_unused; 18265668Skris}; 18376259Sgreen 18465668Skrisenum { 18565668Skris HANDLE_UNUSED, 18665668Skris HANDLE_DIR, 18765668Skris HANDLE_FILE 18865668Skris}; 18976259Sgreen 190181111SdesHandle *handles = NULL; 191181111Sdesu_int num_handles = 0; 192181111Sdesint first_unused_handle = -1; 19365668Skris 194181111Sdesstatic void handle_unused(int i) 19565668Skris{ 196181111Sdes handles[i].use = HANDLE_UNUSED; 197181111Sdes handles[i].next_unused = first_unused_handle; 198181111Sdes first_unused_handle = i; 19965668Skris} 20065668Skris 20192555Sdesstatic int 202126274Sdeshandle_new(int use, const char *name, int fd, DIR *dirp) 20365668Skris{ 204181111Sdes int i; 20576259Sgreen 206181111Sdes if (first_unused_handle == -1) { 207181111Sdes if (num_handles + 1 <= num_handles) 208181111Sdes return -1; 209181111Sdes num_handles++; 210181111Sdes handles = xrealloc(handles, num_handles, sizeof(Handle)); 211181111Sdes handle_unused(num_handles - 1); 21265668Skris } 213181111Sdes 214181111Sdes i = first_unused_handle; 215181111Sdes first_unused_handle = handles[i].next_unused; 216181111Sdes 217181111Sdes handles[i].use = use; 218181111Sdes handles[i].dirp = dirp; 219181111Sdes handles[i].fd = fd; 220181111Sdes handles[i].name = xstrdup(name); 221181111Sdes handles[i].bytes_read = handles[i].bytes_write = 0; 222181111Sdes 223181111Sdes return i; 22465668Skris} 22565668Skris 22692555Sdesstatic int 22765668Skrishandle_is_ok(int i, int type) 22865668Skris{ 229181111Sdes return i >= 0 && (u_int)i < num_handles && handles[i].use == type; 23065668Skris} 23165668Skris 23292555Sdesstatic int 23365668Skrishandle_to_string(int handle, char **stringp, int *hlenp) 23465668Skris{ 23565668Skris if (stringp == NULL || hlenp == NULL) 23665668Skris return -1; 23776259Sgreen *stringp = xmalloc(sizeof(int32_t)); 238162852Sdes put_u32(*stringp, handle); 23976259Sgreen *hlenp = sizeof(int32_t); 24065668Skris return 0; 24165668Skris} 24265668Skris 24392555Sdesstatic int 244126274Sdeshandle_from_string(const char *handle, u_int hlen) 24565668Skris{ 24676259Sgreen int val; 24776259Sgreen 24876259Sgreen if (hlen != sizeof(int32_t)) 24965668Skris return -1; 250162852Sdes val = get_u32(handle); 25165668Skris if (handle_is_ok(val, HANDLE_FILE) || 25265668Skris handle_is_ok(val, HANDLE_DIR)) 25365668Skris return val; 25465668Skris return -1; 25565668Skris} 25665668Skris 25792555Sdesstatic char * 25865668Skrishandle_to_name(int handle) 25965668Skris{ 26065668Skris if (handle_is_ok(handle, HANDLE_DIR)|| 26165668Skris handle_is_ok(handle, HANDLE_FILE)) 26265668Skris return handles[handle].name; 26365668Skris return NULL; 26465668Skris} 26565668Skris 26692555Sdesstatic DIR * 26765668Skrishandle_to_dir(int handle) 26865668Skris{ 26965668Skris if (handle_is_ok(handle, HANDLE_DIR)) 27065668Skris return handles[handle].dirp; 27165668Skris return NULL; 27265668Skris} 27365668Skris 27492555Sdesstatic int 27565668Skrishandle_to_fd(int handle) 27665668Skris{ 27776259Sgreen if (handle_is_ok(handle, HANDLE_FILE)) 27865668Skris return handles[handle].fd; 27965668Skris return -1; 28065668Skris} 28165668Skris 282162852Sdesstatic void 283162852Sdeshandle_update_read(int handle, ssize_t bytes) 284162852Sdes{ 285162852Sdes if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 286162852Sdes handles[handle].bytes_read += bytes; 287162852Sdes} 288162852Sdes 289162852Sdesstatic void 290162852Sdeshandle_update_write(int handle, ssize_t bytes) 291162852Sdes{ 292162852Sdes if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 293162852Sdes handles[handle].bytes_write += bytes; 294162852Sdes} 295162852Sdes 296162852Sdesstatic u_int64_t 297162852Sdeshandle_bytes_read(int handle) 298162852Sdes{ 299162852Sdes if (handle_is_ok(handle, HANDLE_FILE)) 300162852Sdes return (handles[handle].bytes_read); 301162852Sdes return 0; 302162852Sdes} 303162852Sdes 304162852Sdesstatic u_int64_t 305162852Sdeshandle_bytes_write(int handle) 306162852Sdes{ 307162852Sdes if (handle_is_ok(handle, HANDLE_FILE)) 308162852Sdes return (handles[handle].bytes_write); 309162852Sdes return 0; 310162852Sdes} 311162852Sdes 31292555Sdesstatic int 31365668Skrishandle_close(int handle) 31465668Skris{ 31565668Skris int ret = -1; 31676259Sgreen 31765668Skris if (handle_is_ok(handle, HANDLE_FILE)) { 31865668Skris ret = close(handles[handle].fd); 319113908Sdes xfree(handles[handle].name); 320181111Sdes handle_unused(handle); 32165668Skris } else if (handle_is_ok(handle, HANDLE_DIR)) { 32265668Skris ret = closedir(handles[handle].dirp); 323113908Sdes xfree(handles[handle].name); 324181111Sdes handle_unused(handle); 32565668Skris } else { 32665668Skris errno = ENOENT; 32765668Skris } 32865668Skris return ret; 32965668Skris} 33065668Skris 331162852Sdesstatic void 332162852Sdeshandle_log_close(int handle, char *emsg) 333162852Sdes{ 334162852Sdes if (handle_is_ok(handle, HANDLE_FILE)) { 335162852Sdes logit("%s%sclose \"%s\" bytes read %llu written %llu", 336162852Sdes emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 337162852Sdes handle_to_name(handle), 338181111Sdes (unsigned long long)handle_bytes_read(handle), 339181111Sdes (unsigned long long)handle_bytes_write(handle)); 340162852Sdes } else { 341162852Sdes logit("%s%sclosedir \"%s\"", 342162852Sdes emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 343162852Sdes handle_to_name(handle)); 344162852Sdes } 345162852Sdes} 346162852Sdes 347162852Sdesstatic void 348162852Sdeshandle_log_exit(void) 349162852Sdes{ 350162852Sdes u_int i; 351162852Sdes 352181111Sdes for (i = 0; i < num_handles; i++) 353162852Sdes if (handles[i].use != HANDLE_UNUSED) 354162852Sdes handle_log_close(i, "forced"); 355162852Sdes} 356162852Sdes 35792555Sdesstatic int 35865668Skrisget_handle(void) 35965668Skris{ 36065668Skris char *handle; 36176259Sgreen int val = -1; 36265668Skris u_int hlen; 36376259Sgreen 36465668Skris handle = get_string(&hlen); 36576259Sgreen if (hlen < 256) 36676259Sgreen val = handle_from_string(handle, hlen); 36765668Skris xfree(handle); 36865668Skris return val; 36965668Skris} 37065668Skris 37165668Skris/* send replies */ 37265668Skris 37392555Sdesstatic void 37465668Skrissend_msg(Buffer *m) 37565668Skris{ 37665668Skris int mlen = buffer_len(m); 37776259Sgreen 37865668Skris buffer_put_int(&oqueue, mlen); 37965668Skris buffer_append(&oqueue, buffer_ptr(m), mlen); 38065668Skris buffer_consume(m, mlen); 38165668Skris} 38265668Skris 383162852Sdesstatic const char * 384162852Sdesstatus_to_message(u_int32_t status) 38565668Skris{ 38676259Sgreen const char *status_messages[] = { 38776259Sgreen "Success", /* SSH_FX_OK */ 38876259Sgreen "End of file", /* SSH_FX_EOF */ 38976259Sgreen "No such file", /* SSH_FX_NO_SUCH_FILE */ 39076259Sgreen "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 39176259Sgreen "Failure", /* SSH_FX_FAILURE */ 39276259Sgreen "Bad message", /* SSH_FX_BAD_MESSAGE */ 39376259Sgreen "No connection", /* SSH_FX_NO_CONNECTION */ 39476259Sgreen "Connection lost", /* SSH_FX_CONNECTION_LOST */ 39576259Sgreen "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 39676259Sgreen "Unknown error" /* Others */ 39776259Sgreen }; 398162852Sdes return (status_messages[MIN(status,SSH2_FX_MAX)]); 399162852Sdes} 40076259Sgreen 401162852Sdesstatic void 402162852Sdessend_status(u_int32_t id, u_int32_t status) 403162852Sdes{ 404162852Sdes Buffer msg; 405162852Sdes 406162852Sdes debug3("request %u: sent status %u", id, status); 407162852Sdes if (log_level > SYSLOG_LEVEL_VERBOSE || 408162852Sdes (status != SSH2_FX_OK && status != SSH2_FX_EOF)) 409162852Sdes logit("sent status %s", status_to_message(status)); 41065668Skris buffer_init(&msg); 41176259Sgreen buffer_put_char(&msg, SSH2_FXP_STATUS); 41265668Skris buffer_put_int(&msg, id); 413137015Sdes buffer_put_int(&msg, status); 41476259Sgreen if (version >= 3) { 415162852Sdes buffer_put_cstring(&msg, status_to_message(status)); 41676259Sgreen buffer_put_cstring(&msg, ""); 41776259Sgreen } 41865668Skris send_msg(&msg); 41965668Skris buffer_free(&msg); 42065668Skris} 42192555Sdesstatic void 422126274Sdessend_data_or_handle(char type, u_int32_t id, const char *data, int dlen) 42365668Skris{ 42465668Skris Buffer msg; 42576259Sgreen 42665668Skris buffer_init(&msg); 42765668Skris buffer_put_char(&msg, type); 42865668Skris buffer_put_int(&msg, id); 42965668Skris buffer_put_string(&msg, data, dlen); 43065668Skris send_msg(&msg); 43165668Skris buffer_free(&msg); 43265668Skris} 43365668Skris 43492555Sdesstatic void 435126274Sdessend_data(u_int32_t id, const char *data, int dlen) 43665668Skris{ 437162852Sdes debug("request %u: sent data len %d", id, dlen); 43876259Sgreen send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 43965668Skris} 44065668Skris 44192555Sdesstatic void 44265668Skrissend_handle(u_int32_t id, int handle) 44365668Skris{ 44465668Skris char *string; 44565668Skris int hlen; 44676259Sgreen 44765668Skris handle_to_string(handle, &string, &hlen); 448162852Sdes debug("request %u: sent handle handle %d", id, handle); 44976259Sgreen send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 45065668Skris xfree(string); 45165668Skris} 45265668Skris 45392555Sdesstatic void 454126274Sdessend_names(u_int32_t id, int count, const Stat *stats) 45565668Skris{ 45665668Skris Buffer msg; 45765668Skris int i; 45876259Sgreen 45965668Skris buffer_init(&msg); 46076259Sgreen buffer_put_char(&msg, SSH2_FXP_NAME); 46165668Skris buffer_put_int(&msg, id); 46265668Skris buffer_put_int(&msg, count); 463162852Sdes debug("request %u: sent names count %d", id, count); 46465668Skris for (i = 0; i < count; i++) { 46565668Skris buffer_put_cstring(&msg, stats[i].name); 46665668Skris buffer_put_cstring(&msg, stats[i].long_name); 46765668Skris encode_attrib(&msg, &stats[i].attrib); 46865668Skris } 46965668Skris send_msg(&msg); 47065668Skris buffer_free(&msg); 47165668Skris} 47265668Skris 47392555Sdesstatic void 474126274Sdessend_attrib(u_int32_t id, const Attrib *a) 47565668Skris{ 47665668Skris Buffer msg; 47776259Sgreen 478162852Sdes debug("request %u: sent attrib have 0x%x", id, a->flags); 47965668Skris buffer_init(&msg); 48076259Sgreen buffer_put_char(&msg, SSH2_FXP_ATTRS); 48165668Skris buffer_put_int(&msg, id); 48265668Skris encode_attrib(&msg, a); 48365668Skris send_msg(&msg); 48465668Skris buffer_free(&msg); 48565668Skris} 48665668Skris 487181111Sdesstatic void 488181111Sdessend_statvfs(u_int32_t id, struct statvfs *st) 489181111Sdes{ 490181111Sdes Buffer msg; 491181111Sdes u_int64_t flag; 492181111Sdes 493181111Sdes flag = (st->f_flag & ST_RDONLY) ? SSH2_FXE_STATVFS_ST_RDONLY : 0; 494181111Sdes flag |= (st->f_flag & ST_NOSUID) ? SSH2_FXE_STATVFS_ST_NOSUID : 0; 495181111Sdes 496181111Sdes buffer_init(&msg); 497181111Sdes buffer_put_char(&msg, SSH2_FXP_EXTENDED_REPLY); 498181111Sdes buffer_put_int(&msg, id); 499181111Sdes buffer_put_int64(&msg, st->f_bsize); 500181111Sdes buffer_put_int64(&msg, st->f_frsize); 501181111Sdes buffer_put_int64(&msg, st->f_blocks); 502181111Sdes buffer_put_int64(&msg, st->f_bfree); 503181111Sdes buffer_put_int64(&msg, st->f_bavail); 504181111Sdes buffer_put_int64(&msg, st->f_files); 505181111Sdes buffer_put_int64(&msg, st->f_ffree); 506181111Sdes buffer_put_int64(&msg, st->f_favail); 507181111Sdes buffer_put_int64(&msg, FSID_TO_ULONG(st->f_fsid)); 508181111Sdes buffer_put_int64(&msg, flag); 509181111Sdes buffer_put_int64(&msg, st->f_namemax); 510181111Sdes send_msg(&msg); 511181111Sdes buffer_free(&msg); 512181111Sdes} 513181111Sdes 51465668Skris/* parse incoming */ 51565668Skris 51692555Sdesstatic void 51765668Skrisprocess_init(void) 51865668Skris{ 51965668Skris Buffer msg; 52065668Skris 52198675Sdes version = get_int(); 522162852Sdes verbose("received client version %d", version); 52365668Skris buffer_init(&msg); 52476259Sgreen buffer_put_char(&msg, SSH2_FXP_VERSION); 52576259Sgreen buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 526181111Sdes /* POSIX rename extension */ 527181111Sdes buffer_put_cstring(&msg, "posix-rename@openssh.com"); 528181111Sdes buffer_put_cstring(&msg, "1"); /* version */ 529181111Sdes /* statvfs extension */ 530181111Sdes buffer_put_cstring(&msg, "statvfs@openssh.com"); 531181111Sdes buffer_put_cstring(&msg, "2"); /* version */ 532181111Sdes /* fstatvfs extension */ 533181111Sdes buffer_put_cstring(&msg, "fstatvfs@openssh.com"); 534181111Sdes buffer_put_cstring(&msg, "2"); /* version */ 53565668Skris send_msg(&msg); 53665668Skris buffer_free(&msg); 53765668Skris} 53865668Skris 53992555Sdesstatic void 54065668Skrisprocess_open(void) 54165668Skris{ 54265668Skris u_int32_t id, pflags; 54365668Skris Attrib *a; 54465668Skris char *name; 54576259Sgreen int handle, fd, flags, mode, status = SSH2_FX_FAILURE; 54665668Skris 54765668Skris id = get_int(); 54865668Skris name = get_string(NULL); 54976259Sgreen pflags = get_int(); /* portable flags */ 550162852Sdes debug3("request %u: open flags %d", id, pflags); 55165668Skris a = get_attrib(); 55265668Skris flags = flags_from_portable(pflags); 55376259Sgreen mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; 554162852Sdes logit("open \"%s\" flags %s mode 0%o", 555162852Sdes name, string_from_portable(pflags), mode); 55665668Skris fd = open(name, flags, mode); 55765668Skris if (fd < 0) { 55865668Skris status = errno_to_portable(errno); 55965668Skris } else { 560113908Sdes handle = handle_new(HANDLE_FILE, name, fd, NULL); 56165668Skris if (handle < 0) { 56265668Skris close(fd); 56365668Skris } else { 56465668Skris send_handle(id, handle); 56576259Sgreen status = SSH2_FX_OK; 56665668Skris } 56765668Skris } 56876259Sgreen if (status != SSH2_FX_OK) 56965668Skris send_status(id, status); 57065668Skris xfree(name); 57165668Skris} 57265668Skris 57392555Sdesstatic void 57465668Skrisprocess_close(void) 57565668Skris{ 57665668Skris u_int32_t id; 57776259Sgreen int handle, ret, status = SSH2_FX_FAILURE; 57865668Skris 57965668Skris id = get_int(); 58065668Skris handle = get_handle(); 581162852Sdes debug3("request %u: close handle %u", id, handle); 582162852Sdes handle_log_close(handle, NULL); 58365668Skris ret = handle_close(handle); 58476259Sgreen status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 58565668Skris send_status(id, status); 58665668Skris} 58765668Skris 58892555Sdesstatic void 58965668Skrisprocess_read(void) 59065668Skris{ 59165668Skris char buf[64*1024]; 59276259Sgreen u_int32_t id, len; 59376259Sgreen int handle, fd, ret, status = SSH2_FX_FAILURE; 59465668Skris u_int64_t off; 59565668Skris 59665668Skris id = get_int(); 59765668Skris handle = get_handle(); 59876259Sgreen off = get_int64(); 59965668Skris len = get_int(); 60065668Skris 601162852Sdes debug("request %u: read \"%s\" (handle %d) off %llu len %d", 602162852Sdes id, handle_to_name(handle), handle, (unsigned long long)off, len); 60365668Skris if (len > sizeof buf) { 60465668Skris len = sizeof buf; 605162852Sdes debug2("read change len %d", len); 60665668Skris } 60765668Skris fd = handle_to_fd(handle); 60865668Skris if (fd >= 0) { 60965668Skris if (lseek(fd, off, SEEK_SET) < 0) { 61065668Skris error("process_read: seek failed"); 61165668Skris status = errno_to_portable(errno); 61265668Skris } else { 61365668Skris ret = read(fd, buf, len); 61465668Skris if (ret < 0) { 61565668Skris status = errno_to_portable(errno); 61665668Skris } else if (ret == 0) { 61776259Sgreen status = SSH2_FX_EOF; 61865668Skris } else { 61965668Skris send_data(id, buf, ret); 62076259Sgreen status = SSH2_FX_OK; 621162852Sdes handle_update_read(handle, ret); 62265668Skris } 62365668Skris } 62465668Skris } 62576259Sgreen if (status != SSH2_FX_OK) 62665668Skris send_status(id, status); 62765668Skris} 62865668Skris 62992555Sdesstatic void 63065668Skrisprocess_write(void) 63165668Skris{ 63276259Sgreen u_int32_t id; 63365668Skris u_int64_t off; 63465668Skris u_int len; 63576259Sgreen int handle, fd, ret, status = SSH2_FX_FAILURE; 63665668Skris char *data; 63765668Skris 63865668Skris id = get_int(); 63965668Skris handle = get_handle(); 64076259Sgreen off = get_int64(); 64165668Skris data = get_string(&len); 64265668Skris 643162852Sdes debug("request %u: write \"%s\" (handle %d) off %llu len %d", 644162852Sdes id, handle_to_name(handle), handle, (unsigned long long)off, len); 64565668Skris fd = handle_to_fd(handle); 64665668Skris if (fd >= 0) { 64765668Skris if (lseek(fd, off, SEEK_SET) < 0) { 64865668Skris status = errno_to_portable(errno); 64965668Skris error("process_write: seek failed"); 65065668Skris } else { 65165668Skris/* XXX ATOMICIO ? */ 65265668Skris ret = write(fd, data, len); 653149749Sdes if (ret < 0) { 65465668Skris error("process_write: write failed"); 65565668Skris status = errno_to_portable(errno); 656149749Sdes } else if ((size_t)ret == len) { 65776259Sgreen status = SSH2_FX_OK; 658162852Sdes handle_update_write(handle, ret); 65965668Skris } else { 660162852Sdes debug2("nothing at all written"); 66165668Skris } 66265668Skris } 66365668Skris } 66465668Skris send_status(id, status); 66565668Skris xfree(data); 66665668Skris} 66765668Skris 66892555Sdesstatic void 66965668Skrisprocess_do_stat(int do_lstat) 67065668Skris{ 67176259Sgreen Attrib a; 67265668Skris struct stat st; 67365668Skris u_int32_t id; 67465668Skris char *name; 67576259Sgreen int ret, status = SSH2_FX_FAILURE; 67665668Skris 67765668Skris id = get_int(); 67865668Skris name = get_string(NULL); 679162852Sdes debug3("request %u: %sstat", id, do_lstat ? "l" : ""); 680162852Sdes verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); 68165668Skris ret = do_lstat ? lstat(name, &st) : stat(name, &st); 68265668Skris if (ret < 0) { 68365668Skris status = errno_to_portable(errno); 68465668Skris } else { 68576259Sgreen stat_to_attrib(&st, &a); 68676259Sgreen send_attrib(id, &a); 68776259Sgreen status = SSH2_FX_OK; 68865668Skris } 68976259Sgreen if (status != SSH2_FX_OK) 69065668Skris send_status(id, status); 69165668Skris xfree(name); 69265668Skris} 69365668Skris 69492555Sdesstatic void 69565668Skrisprocess_stat(void) 69665668Skris{ 69765668Skris process_do_stat(0); 69865668Skris} 69965668Skris 70092555Sdesstatic void 70165668Skrisprocess_lstat(void) 70265668Skris{ 70365668Skris process_do_stat(1); 70465668Skris} 70565668Skris 70692555Sdesstatic void 70765668Skrisprocess_fstat(void) 70865668Skris{ 70976259Sgreen Attrib a; 71065668Skris struct stat st; 71165668Skris u_int32_t id; 71276259Sgreen int fd, ret, handle, status = SSH2_FX_FAILURE; 71365668Skris 71465668Skris id = get_int(); 71565668Skris handle = get_handle(); 716162852Sdes debug("request %u: fstat \"%s\" (handle %u)", 717162852Sdes id, handle_to_name(handle), handle); 71865668Skris fd = handle_to_fd(handle); 719181111Sdes if (fd >= 0) { 72065668Skris ret = fstat(fd, &st); 72165668Skris if (ret < 0) { 72265668Skris status = errno_to_portable(errno); 72365668Skris } else { 72476259Sgreen stat_to_attrib(&st, &a); 72576259Sgreen send_attrib(id, &a); 72676259Sgreen status = SSH2_FX_OK; 72765668Skris } 72865668Skris } 72976259Sgreen if (status != SSH2_FX_OK) 73065668Skris send_status(id, status); 73165668Skris} 73265668Skris 73392555Sdesstatic struct timeval * 734126274Sdesattrib_to_tv(const Attrib *a) 73565668Skris{ 73665668Skris static struct timeval tv[2]; 73776259Sgreen 73865668Skris tv[0].tv_sec = a->atime; 73965668Skris tv[0].tv_usec = 0; 74065668Skris tv[1].tv_sec = a->mtime; 74165668Skris tv[1].tv_usec = 0; 74265668Skris return tv; 74365668Skris} 74465668Skris 74592555Sdesstatic void 74665668Skrisprocess_setstat(void) 74765668Skris{ 74865668Skris Attrib *a; 74965668Skris u_int32_t id; 75065668Skris char *name; 75199060Sdes int status = SSH2_FX_OK, ret; 75265668Skris 75365668Skris id = get_int(); 75465668Skris name = get_string(NULL); 75565668Skris a = get_attrib(); 756162852Sdes debug("request %u: setstat name \"%s\"", id, name); 75792555Sdes if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 758181111Sdes logit("set \"%s\" size %llu", 759181111Sdes name, (unsigned long long)a->size); 76092555Sdes ret = truncate(name, a->size); 76192555Sdes if (ret == -1) 76292555Sdes status = errno_to_portable(errno); 76392555Sdes } 76476259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 765162852Sdes logit("set \"%s\" mode %04o", name, a->perm); 766181111Sdes ret = chmod(name, a->perm & 07777); 76765668Skris if (ret == -1) 76865668Skris status = errno_to_portable(errno); 76965668Skris } 77076259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 771162852Sdes char buf[64]; 772162852Sdes time_t t = a->mtime; 773162852Sdes 774162852Sdes strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 775162852Sdes localtime(&t)); 776162852Sdes logit("set \"%s\" modtime %s", name, buf); 77765668Skris ret = utimes(name, attrib_to_tv(a)); 77865668Skris if (ret == -1) 77965668Skris status = errno_to_portable(errno); 78065668Skris } 78176259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 782162852Sdes logit("set \"%s\" owner %lu group %lu", name, 783162852Sdes (u_long)a->uid, (u_long)a->gid); 78476259Sgreen ret = chown(name, a->uid, a->gid); 78576259Sgreen if (ret == -1) 78676259Sgreen status = errno_to_portable(errno); 78776259Sgreen } 78865668Skris send_status(id, status); 78965668Skris xfree(name); 79065668Skris} 79165668Skris 79292555Sdesstatic void 79365668Skrisprocess_fsetstat(void) 79465668Skris{ 79565668Skris Attrib *a; 79665668Skris u_int32_t id; 79765668Skris int handle, fd, ret; 79876259Sgreen int status = SSH2_FX_OK; 79965668Skris 80065668Skris id = get_int(); 80165668Skris handle = get_handle(); 80265668Skris a = get_attrib(); 803162852Sdes debug("request %u: fsetstat handle %d", id, handle); 80465668Skris fd = handle_to_fd(handle); 805162852Sdes if (fd < 0) { 80676259Sgreen status = SSH2_FX_FAILURE; 80765668Skris } else { 808162852Sdes char *name = handle_to_name(handle); 809162852Sdes 81092555Sdes if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 811181111Sdes logit("set \"%s\" size %llu", 812181111Sdes name, (unsigned long long)a->size); 81392555Sdes ret = ftruncate(fd, a->size); 81492555Sdes if (ret == -1) 81592555Sdes status = errno_to_portable(errno); 81692555Sdes } 81776259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 818162852Sdes logit("set \"%s\" mode %04o", name, a->perm); 81998937Sdes#ifdef HAVE_FCHMOD 820181111Sdes ret = fchmod(fd, a->perm & 07777); 82198937Sdes#else 822181111Sdes ret = chmod(name, a->perm & 07777); 82398937Sdes#endif 82465668Skris if (ret == -1) 82565668Skris status = errno_to_portable(errno); 82665668Skris } 82776259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 828162852Sdes char buf[64]; 829162852Sdes time_t t = a->mtime; 830162852Sdes 831162852Sdes strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", 832162852Sdes localtime(&t)); 833162852Sdes logit("set \"%s\" modtime %s", name, buf); 83498937Sdes#ifdef HAVE_FUTIMES 83565668Skris ret = futimes(fd, attrib_to_tv(a)); 83698937Sdes#else 83798937Sdes ret = utimes(name, attrib_to_tv(a)); 83898937Sdes#endif 83965668Skris if (ret == -1) 84065668Skris status = errno_to_portable(errno); 84165668Skris } 84276259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 843162852Sdes logit("set \"%s\" owner %lu group %lu", name, 844162852Sdes (u_long)a->uid, (u_long)a->gid); 84598937Sdes#ifdef HAVE_FCHOWN 84676259Sgreen ret = fchown(fd, a->uid, a->gid); 84798937Sdes#else 84898937Sdes ret = chown(name, a->uid, a->gid); 84998937Sdes#endif 85076259Sgreen if (ret == -1) 85176259Sgreen status = errno_to_portable(errno); 85276259Sgreen } 85365668Skris } 85465668Skris send_status(id, status); 85565668Skris} 85665668Skris 85792555Sdesstatic void 85865668Skrisprocess_opendir(void) 85965668Skris{ 86065668Skris DIR *dirp = NULL; 86165668Skris char *path; 86276259Sgreen int handle, status = SSH2_FX_FAILURE; 86365668Skris u_int32_t id; 86465668Skris 86565668Skris id = get_int(); 86665668Skris path = get_string(NULL); 867162852Sdes debug3("request %u: opendir", id); 868162852Sdes logit("opendir \"%s\"", path); 86976259Sgreen dirp = opendir(path); 87065668Skris if (dirp == NULL) { 87165668Skris status = errno_to_portable(errno); 87265668Skris } else { 873113908Sdes handle = handle_new(HANDLE_DIR, path, 0, dirp); 87465668Skris if (handle < 0) { 87565668Skris closedir(dirp); 87665668Skris } else { 87765668Skris send_handle(id, handle); 87876259Sgreen status = SSH2_FX_OK; 87965668Skris } 88076259Sgreen 88165668Skris } 88276259Sgreen if (status != SSH2_FX_OK) 88365668Skris send_status(id, status); 88465668Skris xfree(path); 88565668Skris} 88665668Skris 88792555Sdesstatic void 88865668Skrisprocess_readdir(void) 88965668Skris{ 89065668Skris DIR *dirp; 89165668Skris struct dirent *dp; 89265668Skris char *path; 89365668Skris int handle; 89465668Skris u_int32_t id; 89565668Skris 89665668Skris id = get_int(); 89765668Skris handle = get_handle(); 898162852Sdes debug("request %u: readdir \"%s\" (handle %d)", id, 899162852Sdes handle_to_name(handle), handle); 90065668Skris dirp = handle_to_dir(handle); 90165668Skris path = handle_to_name(handle); 90265668Skris if (dirp == NULL || path == NULL) { 90376259Sgreen send_status(id, SSH2_FX_FAILURE); 90465668Skris } else { 90565668Skris struct stat st; 906162852Sdes char pathname[MAXPATHLEN]; 90765668Skris Stat *stats; 90865668Skris int nstats = 10, count = 0, i; 90999060Sdes 910162852Sdes stats = xcalloc(nstats, sizeof(Stat)); 91165668Skris while ((dp = readdir(dirp)) != NULL) { 91265668Skris if (count >= nstats) { 91365668Skris nstats *= 2; 914162852Sdes stats = xrealloc(stats, nstats, sizeof(Stat)); 91565668Skris } 91665668Skris/* XXX OVERFLOW ? */ 91792555Sdes snprintf(pathname, sizeof pathname, "%s%s%s", path, 91892555Sdes strcmp(path, "/") ? "/" : "", dp->d_name); 91965668Skris if (lstat(pathname, &st) < 0) 92065668Skris continue; 92176259Sgreen stat_to_attrib(&st, &(stats[count].attrib)); 92265668Skris stats[count].name = xstrdup(dp->d_name); 923106121Sdes stats[count].long_name = ls_file(dp->d_name, &st, 0); 92465668Skris count++; 92565668Skris /* send up to 100 entries in one message */ 92676259Sgreen /* XXX check packet size instead */ 92765668Skris if (count == 100) 92865668Skris break; 92965668Skris } 93076259Sgreen if (count > 0) { 93176259Sgreen send_names(id, count, stats); 93292555Sdes for (i = 0; i < count; i++) { 93376259Sgreen xfree(stats[i].name); 93476259Sgreen xfree(stats[i].long_name); 93576259Sgreen } 93676259Sgreen } else { 93776259Sgreen send_status(id, SSH2_FX_EOF); 93865668Skris } 93965668Skris xfree(stats); 94065668Skris } 94165668Skris} 94265668Skris 94392555Sdesstatic void 94465668Skrisprocess_remove(void) 94565668Skris{ 94665668Skris char *name; 94765668Skris u_int32_t id; 94876259Sgreen int status = SSH2_FX_FAILURE; 94965668Skris int ret; 95065668Skris 95165668Skris id = get_int(); 95265668Skris name = get_string(NULL); 953162852Sdes debug3("request %u: remove", id); 954162852Sdes logit("remove name \"%s\"", name); 95576259Sgreen ret = unlink(name); 95676259Sgreen status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 95765668Skris send_status(id, status); 95865668Skris xfree(name); 95965668Skris} 96065668Skris 96192555Sdesstatic void 96265668Skrisprocess_mkdir(void) 96365668Skris{ 96465668Skris Attrib *a; 96565668Skris u_int32_t id; 96665668Skris char *name; 96776259Sgreen int ret, mode, status = SSH2_FX_FAILURE; 96865668Skris 96965668Skris id = get_int(); 97065668Skris name = get_string(NULL); 97165668Skris a = get_attrib(); 97276259Sgreen mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 973181111Sdes a->perm & 07777 : 0777; 974162852Sdes debug3("request %u: mkdir", id); 975162852Sdes logit("mkdir name \"%s\" mode 0%o", name, mode); 97665668Skris ret = mkdir(name, mode); 97776259Sgreen status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 97865668Skris send_status(id, status); 97965668Skris xfree(name); 98065668Skris} 98165668Skris 98292555Sdesstatic void 98365668Skrisprocess_rmdir(void) 98465668Skris{ 98565668Skris u_int32_t id; 98665668Skris char *name; 98765668Skris int ret, status; 98865668Skris 98965668Skris id = get_int(); 99065668Skris name = get_string(NULL); 991162852Sdes debug3("request %u: rmdir", id); 992162852Sdes logit("rmdir name \"%s\"", name); 99365668Skris ret = rmdir(name); 99476259Sgreen status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 99565668Skris send_status(id, status); 99665668Skris xfree(name); 99765668Skris} 99865668Skris 99992555Sdesstatic void 100065668Skrisprocess_realpath(void) 100165668Skris{ 100265668Skris char resolvedname[MAXPATHLEN]; 100365668Skris u_int32_t id; 100465668Skris char *path; 100565668Skris 100665668Skris id = get_int(); 100765668Skris path = get_string(NULL); 100876259Sgreen if (path[0] == '\0') { 100976259Sgreen xfree(path); 101076259Sgreen path = xstrdup("."); 101176259Sgreen } 1012162852Sdes debug3("request %u: realpath", id); 1013162852Sdes verbose("realpath \"%s\"", path); 101465668Skris if (realpath(path, resolvedname) == NULL) { 101565668Skris send_status(id, errno_to_portable(errno)); 101665668Skris } else { 101765668Skris Stat s; 101865668Skris attrib_clear(&s.attrib); 101965668Skris s.name = s.long_name = resolvedname; 102065668Skris send_names(id, 1, &s); 102165668Skris } 102265668Skris xfree(path); 102365668Skris} 102465668Skris 102592555Sdesstatic void 102665668Skrisprocess_rename(void) 102765668Skris{ 102865668Skris u_int32_t id; 102965668Skris char *oldpath, *newpath; 1030113908Sdes int status; 1031113908Sdes struct stat sb; 103265668Skris 103365668Skris id = get_int(); 103465668Skris oldpath = get_string(NULL); 103565668Skris newpath = get_string(NULL); 1036162852Sdes debug3("request %u: rename", id); 1037162852Sdes logit("rename old \"%s\" new \"%s\"", oldpath, newpath); 1038113908Sdes status = SSH2_FX_FAILURE; 1039113908Sdes if (lstat(oldpath, &sb) == -1) 1040113908Sdes status = errno_to_portable(errno); 1041113908Sdes else if (S_ISREG(sb.st_mode)) { 1042113908Sdes /* Race-free rename of regular files */ 1043137015Sdes if (link(oldpath, newpath) == -1) { 1044197679Sdes if (errno == EOPNOTSUPP || errno == ENOSYS 1045181111Sdes#ifdef EXDEV 1046181111Sdes || errno == EXDEV 1047181111Sdes#endif 1048137015Sdes#ifdef LINK_OPNOTSUPP_ERRNO 1049137015Sdes || errno == LINK_OPNOTSUPP_ERRNO 1050137015Sdes#endif 1051137015Sdes ) { 1052137015Sdes struct stat st; 1053137015Sdes 1054137015Sdes /* 1055137015Sdes * fs doesn't support links, so fall back to 1056137015Sdes * stat+rename. This is racy. 1057137015Sdes */ 1058137015Sdes if (stat(newpath, &st) == -1) { 1059137015Sdes if (rename(oldpath, newpath) == -1) 1060137015Sdes status = 1061137015Sdes errno_to_portable(errno); 1062137015Sdes else 1063137015Sdes status = SSH2_FX_OK; 1064137015Sdes } 1065137015Sdes } else { 1066137015Sdes status = errno_to_portable(errno); 1067137015Sdes } 1068137015Sdes } else if (unlink(oldpath) == -1) { 1069113908Sdes status = errno_to_portable(errno); 1070113908Sdes /* clean spare link */ 1071113908Sdes unlink(newpath); 1072113908Sdes } else 1073113908Sdes status = SSH2_FX_OK; 1074113908Sdes } else if (stat(newpath, &sb) == -1) { 1075113908Sdes if (rename(oldpath, newpath) == -1) 1076113908Sdes status = errno_to_portable(errno); 1077113908Sdes else 1078113908Sdes status = SSH2_FX_OK; 107976259Sgreen } 108065668Skris send_status(id, status); 108165668Skris xfree(oldpath); 108265668Skris xfree(newpath); 108365668Skris} 108465668Skris 108592555Sdesstatic void 108676259Sgreenprocess_readlink(void) 108776259Sgreen{ 108876259Sgreen u_int32_t id; 108992555Sdes int len; 1090137015Sdes char buf[MAXPATHLEN]; 109176259Sgreen char *path; 109265668Skris 109376259Sgreen id = get_int(); 109476259Sgreen path = get_string(NULL); 1095162852Sdes debug3("request %u: readlink", id); 1096162852Sdes verbose("readlink \"%s\"", path); 1097137015Sdes if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) 109876259Sgreen send_status(id, errno_to_portable(errno)); 109976259Sgreen else { 110076259Sgreen Stat s; 110192555Sdes 1102137015Sdes buf[len] = '\0'; 110376259Sgreen attrib_clear(&s.attrib); 1104137015Sdes s.name = s.long_name = buf; 110576259Sgreen send_names(id, 1, &s); 110676259Sgreen } 110776259Sgreen xfree(path); 110876259Sgreen} 110976259Sgreen 111092555Sdesstatic void 111176259Sgreenprocess_symlink(void) 111276259Sgreen{ 111376259Sgreen u_int32_t id; 111476259Sgreen char *oldpath, *newpath; 1115113908Sdes int ret, status; 111676259Sgreen 111776259Sgreen id = get_int(); 111876259Sgreen oldpath = get_string(NULL); 111976259Sgreen newpath = get_string(NULL); 1120162852Sdes debug3("request %u: symlink", id); 1121162852Sdes logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); 1122113908Sdes /* this will fail if 'newpath' exists */ 1123113908Sdes ret = symlink(oldpath, newpath); 1124113908Sdes status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 112576259Sgreen send_status(id, status); 112676259Sgreen xfree(oldpath); 112776259Sgreen xfree(newpath); 112876259Sgreen} 112976259Sgreen 113092555Sdesstatic void 1131181111Sdesprocess_extended_posix_rename(u_int32_t id) 1132181111Sdes{ 1133181111Sdes char *oldpath, *newpath; 1134181111Sdes 1135181111Sdes oldpath = get_string(NULL); 1136181111Sdes newpath = get_string(NULL); 1137181111Sdes debug3("request %u: posix-rename", id); 1138181111Sdes logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); 1139181111Sdes if (rename(oldpath, newpath) == -1) 1140181111Sdes send_status(id, errno_to_portable(errno)); 1141181111Sdes else 1142181111Sdes send_status(id, SSH2_FX_OK); 1143181111Sdes xfree(oldpath); 1144181111Sdes xfree(newpath); 1145181111Sdes} 1146181111Sdes 1147181111Sdesstatic void 1148181111Sdesprocess_extended_statvfs(u_int32_t id) 1149181111Sdes{ 1150181111Sdes char *path; 1151181111Sdes struct statvfs st; 1152181111Sdes 1153181111Sdes path = get_string(NULL); 1154181111Sdes debug3("request %u: statfs", id); 1155181111Sdes logit("statfs \"%s\"", path); 1156181111Sdes 1157181111Sdes if (statvfs(path, &st) != 0) 1158181111Sdes send_status(id, errno_to_portable(errno)); 1159181111Sdes else 1160181111Sdes send_statvfs(id, &st); 1161181111Sdes xfree(path); 1162181111Sdes} 1163181111Sdes 1164181111Sdesstatic void 1165181111Sdesprocess_extended_fstatvfs(u_int32_t id) 1166181111Sdes{ 1167181111Sdes int handle, fd; 1168181111Sdes struct statvfs st; 1169181111Sdes 1170181111Sdes handle = get_handle(); 1171181111Sdes debug("request %u: fstatvfs \"%s\" (handle %u)", 1172181111Sdes id, handle_to_name(handle), handle); 1173181111Sdes if ((fd = handle_to_fd(handle)) < 0) { 1174181111Sdes send_status(id, SSH2_FX_FAILURE); 1175181111Sdes return; 1176181111Sdes } 1177181111Sdes if (fstatvfs(fd, &st) != 0) 1178181111Sdes send_status(id, errno_to_portable(errno)); 1179181111Sdes else 1180181111Sdes send_statvfs(id, &st); 1181181111Sdes} 1182181111Sdes 1183181111Sdesstatic void 118476259Sgreenprocess_extended(void) 118576259Sgreen{ 118676259Sgreen u_int32_t id; 118776259Sgreen char *request; 118876259Sgreen 118976259Sgreen id = get_int(); 119076259Sgreen request = get_string(NULL); 1191181111Sdes if (strcmp(request, "posix-rename@openssh.com") == 0) 1192181111Sdes process_extended_posix_rename(id); 1193181111Sdes else if (strcmp(request, "statvfs@openssh.com") == 0) 1194181111Sdes process_extended_statvfs(id); 1195181111Sdes else if (strcmp(request, "fstatvfs@openssh.com") == 0) 1196181111Sdes process_extended_fstatvfs(id); 1197181111Sdes else 1198181111Sdes send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 119976259Sgreen xfree(request); 120076259Sgreen} 120176259Sgreen 120265668Skris/* stolen from ssh-agent */ 120365668Skris 120492555Sdesstatic void 120565668Skrisprocess(void) 120665668Skris{ 120776259Sgreen u_int msg_len; 120898675Sdes u_int buf_len; 120998675Sdes u_int consumed; 121076259Sgreen u_int type; 121176259Sgreen u_char *cp; 121265668Skris 121398675Sdes buf_len = buffer_len(&iqueue); 121498675Sdes if (buf_len < 5) 121565668Skris return; /* Incomplete message. */ 121692555Sdes cp = buffer_ptr(&iqueue); 1217162852Sdes msg_len = get_u32(cp); 1218157016Sdes if (msg_len > SFTP_MAX_MSG_LENGTH) { 1219162852Sdes error("bad message from %s local user %s", 1220162852Sdes client_addr, pw->pw_name); 1221181111Sdes sftp_server_cleanup_exit(11); 122265668Skris } 122398675Sdes if (buf_len < msg_len + 4) 122465668Skris return; 122565668Skris buffer_consume(&iqueue, 4); 122698675Sdes buf_len -= 4; 122765668Skris type = buffer_get_char(&iqueue); 122865668Skris switch (type) { 122976259Sgreen case SSH2_FXP_INIT: 123065668Skris process_init(); 123165668Skris break; 123276259Sgreen case SSH2_FXP_OPEN: 123365668Skris process_open(); 123465668Skris break; 123576259Sgreen case SSH2_FXP_CLOSE: 123665668Skris process_close(); 123765668Skris break; 123876259Sgreen case SSH2_FXP_READ: 123965668Skris process_read(); 124065668Skris break; 124176259Sgreen case SSH2_FXP_WRITE: 124265668Skris process_write(); 124365668Skris break; 124476259Sgreen case SSH2_FXP_LSTAT: 124565668Skris process_lstat(); 124665668Skris break; 124776259Sgreen case SSH2_FXP_FSTAT: 124865668Skris process_fstat(); 124965668Skris break; 125076259Sgreen case SSH2_FXP_SETSTAT: 125165668Skris process_setstat(); 125265668Skris break; 125376259Sgreen case SSH2_FXP_FSETSTAT: 125465668Skris process_fsetstat(); 125565668Skris break; 125676259Sgreen case SSH2_FXP_OPENDIR: 125765668Skris process_opendir(); 125865668Skris break; 125976259Sgreen case SSH2_FXP_READDIR: 126065668Skris process_readdir(); 126165668Skris break; 126276259Sgreen case SSH2_FXP_REMOVE: 126365668Skris process_remove(); 126465668Skris break; 126576259Sgreen case SSH2_FXP_MKDIR: 126665668Skris process_mkdir(); 126765668Skris break; 126876259Sgreen case SSH2_FXP_RMDIR: 126965668Skris process_rmdir(); 127065668Skris break; 127176259Sgreen case SSH2_FXP_REALPATH: 127265668Skris process_realpath(); 127365668Skris break; 127476259Sgreen case SSH2_FXP_STAT: 127565668Skris process_stat(); 127665668Skris break; 127776259Sgreen case SSH2_FXP_RENAME: 127865668Skris process_rename(); 127965668Skris break; 128076259Sgreen case SSH2_FXP_READLINK: 128176259Sgreen process_readlink(); 128276259Sgreen break; 128376259Sgreen case SSH2_FXP_SYMLINK: 128476259Sgreen process_symlink(); 128576259Sgreen break; 128676259Sgreen case SSH2_FXP_EXTENDED: 128776259Sgreen process_extended(); 128876259Sgreen break; 128965668Skris default: 129065668Skris error("Unknown message %d", type); 129165668Skris break; 129265668Skris } 129398675Sdes /* discard the remaining bytes from the current packet */ 1294181111Sdes if (buf_len < buffer_len(&iqueue)) { 1295181111Sdes error("iqueue grew unexpectedly"); 1296181111Sdes sftp_server_cleanup_exit(255); 1297181111Sdes } 129898675Sdes consumed = buf_len - buffer_len(&iqueue); 1299181111Sdes if (msg_len < consumed) { 1300181111Sdes error("msg_len %d < consumed %d", msg_len, consumed); 1301181111Sdes sftp_server_cleanup_exit(255); 1302181111Sdes } 130398675Sdes if (msg_len > consumed) 130498675Sdes buffer_consume(&iqueue, msg_len - consumed); 130565668Skris} 130665668Skris 1307162852Sdes/* Cleanup handler that logs active handles upon normal exit */ 1308162852Sdesvoid 1309181111Sdessftp_server_cleanup_exit(int i) 1310162852Sdes{ 1311162852Sdes if (pw != NULL && client_addr != NULL) { 1312162852Sdes handle_log_exit(); 1313162852Sdes logit("session closed for local user %s from [%s]", 1314162852Sdes pw->pw_name, client_addr); 1315162852Sdes } 1316162852Sdes _exit(i); 1317162852Sdes} 1318162852Sdes 1319162852Sdesstatic void 1320181111Sdessftp_server_usage(void) 1321162852Sdes{ 1322162852Sdes extern char *__progname; 1323162852Sdes 1324162852Sdes fprintf(stderr, 1325162852Sdes "usage: %s [-he] [-l log_level] [-f log_facility]\n", __progname); 1326162852Sdes exit(1); 1327162852Sdes} 1328162852Sdes 132965668Skrisint 1330181111Sdessftp_server_main(int argc, char **argv, struct passwd *user_pw) 133165668Skris{ 133276259Sgreen fd_set *rset, *wset; 1333162852Sdes int in, out, max, ch, skipargs = 0, log_stderr = 0; 133476259Sgreen ssize_t len, olen, set_size; 1335162852Sdes SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; 1336181111Sdes char *cp, buf[4*4096]; 133765668Skris 1338162852Sdes extern char *optarg; 1339162852Sdes extern char *__progname; 1340162852Sdes 1341162852Sdes __progname = ssh_get_progname(argv[0]); 1342162852Sdes log_init(__progname, log_level, log_facility, log_stderr); 134376259Sgreen 1344197679Sdes while (!skipargs && (ch = getopt(argc, argv, "f:l:che")) != -1) { 1345162852Sdes switch (ch) { 1346162852Sdes case 'c': 1347162852Sdes /* 1348162852Sdes * Ignore all arguments if we are invoked as a 1349162852Sdes * shell using "sftp-server -c command" 1350162852Sdes */ 1351162852Sdes skipargs = 1; 1352162852Sdes break; 1353162852Sdes case 'e': 1354162852Sdes log_stderr = 1; 1355162852Sdes break; 1356162852Sdes case 'l': 1357162852Sdes log_level = log_level_number(optarg); 1358162852Sdes if (log_level == SYSLOG_LEVEL_NOT_SET) 1359162852Sdes error("Invalid log level \"%s\"", optarg); 1360162852Sdes break; 1361162852Sdes case 'f': 1362162852Sdes log_facility = log_facility_number(optarg); 1363181111Sdes if (log_facility == SYSLOG_FACILITY_NOT_SET) 1364162852Sdes error("Invalid log facility \"%s\"", optarg); 1365162852Sdes break; 1366162852Sdes case 'h': 1367162852Sdes default: 1368181111Sdes sftp_server_usage(); 1369162852Sdes } 1370162852Sdes } 1371162852Sdes 1372162852Sdes log_init(__progname, log_level, log_facility, log_stderr); 1373162852Sdes 1374162852Sdes if ((cp = getenv("SSH_CONNECTION")) != NULL) { 1375162852Sdes client_addr = xstrdup(cp); 1376181111Sdes if ((cp = strchr(client_addr, ' ')) == NULL) { 1377181111Sdes error("Malformed SSH_CONNECTION variable: \"%s\"", 1378162852Sdes getenv("SSH_CONNECTION")); 1379181111Sdes sftp_server_cleanup_exit(255); 1380181111Sdes } 1381162852Sdes *cp = '\0'; 1382162852Sdes } else 1383162852Sdes client_addr = xstrdup("UNKNOWN"); 1384162852Sdes 1385181111Sdes pw = pwcopy(user_pw); 1386162852Sdes 1387162852Sdes logit("session opened for local user %s from [%s]", 1388162852Sdes pw->pw_name, client_addr); 1389162852Sdes 139065668Skris in = dup(STDIN_FILENO); 139165668Skris out = dup(STDOUT_FILENO); 139265668Skris 139398937Sdes#ifdef HAVE_CYGWIN 139498937Sdes setmode(in, O_BINARY); 139598937Sdes setmode(out, O_BINARY); 139698937Sdes#endif 139798937Sdes 139865668Skris max = 0; 139965668Skris if (in > max) 140065668Skris max = in; 140165668Skris if (out > max) 140265668Skris max = out; 140365668Skris 140465668Skris buffer_init(&iqueue); 140565668Skris buffer_init(&oqueue); 140665668Skris 140776259Sgreen set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 140876259Sgreen rset = (fd_set *)xmalloc(set_size); 140976259Sgreen wset = (fd_set *)xmalloc(set_size); 141076259Sgreen 141165668Skris for (;;) { 141276259Sgreen memset(rset, 0, set_size); 141376259Sgreen memset(wset, 0, set_size); 141465668Skris 1415181111Sdes /* 1416181111Sdes * Ensure that we can read a full buffer and handle 1417181111Sdes * the worst-case length packet it can generate, 1418181111Sdes * otherwise apply backpressure by stopping reads. 1419181111Sdes */ 1420181111Sdes if (buffer_check_alloc(&iqueue, sizeof(buf)) && 1421181111Sdes buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) 1422181111Sdes FD_SET(in, rset); 1423181111Sdes 142465668Skris olen = buffer_len(&oqueue); 142565668Skris if (olen > 0) 142676259Sgreen FD_SET(out, wset); 142765668Skris 142876259Sgreen if (select(max+1, rset, wset, NULL, NULL) < 0) { 142965668Skris if (errno == EINTR) 143065668Skris continue; 1431162852Sdes error("select: %s", strerror(errno)); 1432181111Sdes sftp_server_cleanup_exit(2); 143365668Skris } 143465668Skris 143565668Skris /* copy stdin to iqueue */ 143676259Sgreen if (FD_ISSET(in, rset)) { 143765668Skris len = read(in, buf, sizeof buf); 143865668Skris if (len == 0) { 143965668Skris debug("read eof"); 1440181111Sdes sftp_server_cleanup_exit(0); 144165668Skris } else if (len < 0) { 1442162852Sdes error("read: %s", strerror(errno)); 1443181111Sdes sftp_server_cleanup_exit(1); 144465668Skris } else { 144565668Skris buffer_append(&iqueue, buf, len); 144665668Skris } 144765668Skris } 144865668Skris /* send oqueue to stdout */ 144976259Sgreen if (FD_ISSET(out, wset)) { 145065668Skris len = write(out, buffer_ptr(&oqueue), olen); 145165668Skris if (len < 0) { 1452162852Sdes error("write: %s", strerror(errno)); 1453181111Sdes sftp_server_cleanup_exit(1); 145465668Skris } else { 145565668Skris buffer_consume(&oqueue, len); 145665668Skris } 145765668Skris } 1458181111Sdes 1459181111Sdes /* 1460181111Sdes * Process requests from client if we can fit the results 1461181111Sdes * into the output buffer, otherwise stop processing input 1462181111Sdes * and let the output queue drain. 1463181111Sdes */ 1464181111Sdes if (buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) 1465181111Sdes process(); 146665668Skris } 146765668Skris} 1468