sftp-server.c revision 124208
165668Skris/* 292555Sdes * Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved. 365668Skris * 465668Skris * Redistribution and use in source and binary forms, with or without 565668Skris * modification, are permitted provided that the following conditions 665668Skris * are met: 765668Skris * 1. Redistributions of source code must retain the above copyright 865668Skris * notice, this list of conditions and the following disclaimer. 965668Skris * 2. Redistributions in binary form must reproduce the above copyright 1065668Skris * notice, this list of conditions and the following disclaimer in the 1165668Skris * documentation and/or other materials provided with the distribution. 1265668Skris * 1365668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1465668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1565668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1665668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1765668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 1865668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 1965668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2065668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2165668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2265668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2365668Skris */ 2465668Skris#include "includes.h" 25124208SdesRCSID("$OpenBSD: sftp-server.c,v 1.43 2003/06/25 22:39:36 miod Exp $"); 2665668Skris 2765668Skris#include "buffer.h" 2865668Skris#include "bufaux.h" 2965668Skris#include "getput.h" 3076259Sgreen#include "log.h" 3165668Skris#include "xmalloc.h" 3265668Skris 3376259Sgreen#include "sftp.h" 3476259Sgreen#include "sftp-common.h" 3565668Skris 3665668Skris/* helper */ 3776259Sgreen#define get_int64() buffer_get_int64(&iqueue); 3865668Skris#define get_int() buffer_get_int(&iqueue); 3965668Skris#define get_string(lenp) buffer_get_string(&iqueue, lenp); 4076259Sgreen#define TRACE debug 4165668Skris 4298937Sdes#ifdef HAVE___PROGNAME 4398937Sdesextern char *__progname; 4498937Sdes#else 4598937Sdeschar *__progname; 4698937Sdes#endif 4798937Sdes 4865668Skris/* input and output queue */ 4965668SkrisBuffer iqueue; 5065668SkrisBuffer oqueue; 5165668Skris 5276259Sgreen/* Version of client */ 5376259Sgreenint version; 5476259Sgreen 55124208Sdes/* portable attributes, etc. */ 5665668Skris 5765668Skristypedef struct Stat Stat; 5865668Skris 5976259Sgreenstruct Stat { 6065668Skris char *name; 6165668Skris char *long_name; 6265668Skris Attrib attrib; 6365668Skris}; 6465668Skris 6592555Sdesstatic int 6665668Skriserrno_to_portable(int unixerrno) 6765668Skris{ 6865668Skris int ret = 0; 6976259Sgreen 7065668Skris switch (unixerrno) { 7165668Skris case 0: 7276259Sgreen ret = SSH2_FX_OK; 7365668Skris break; 7465668Skris case ENOENT: 7565668Skris case ENOTDIR: 7665668Skris case EBADF: 7765668Skris case ELOOP: 7876259Sgreen ret = SSH2_FX_NO_SUCH_FILE; 7965668Skris break; 8065668Skris case EPERM: 8165668Skris case EACCES: 8265668Skris case EFAULT: 8376259Sgreen ret = SSH2_FX_PERMISSION_DENIED; 8465668Skris break; 8565668Skris case ENAMETOOLONG: 8665668Skris case EINVAL: 8776259Sgreen ret = SSH2_FX_BAD_MESSAGE; 8865668Skris break; 8965668Skris default: 9076259Sgreen ret = SSH2_FX_FAILURE; 9165668Skris break; 9265668Skris } 9365668Skris return ret; 9465668Skris} 9565668Skris 9692555Sdesstatic int 9765668Skrisflags_from_portable(int pflags) 9865668Skris{ 9965668Skris int flags = 0; 10076259Sgreen 10176259Sgreen if ((pflags & SSH2_FXF_READ) && 10276259Sgreen (pflags & SSH2_FXF_WRITE)) { 10365668Skris flags = O_RDWR; 10476259Sgreen } else if (pflags & SSH2_FXF_READ) { 10565668Skris flags = O_RDONLY; 10676259Sgreen } else if (pflags & SSH2_FXF_WRITE) { 10765668Skris flags = O_WRONLY; 10865668Skris } 10976259Sgreen if (pflags & SSH2_FXF_CREAT) 11065668Skris flags |= O_CREAT; 11176259Sgreen if (pflags & SSH2_FXF_TRUNC) 11265668Skris flags |= O_TRUNC; 11376259Sgreen if (pflags & SSH2_FXF_EXCL) 11465668Skris flags |= O_EXCL; 11565668Skris return flags; 11665668Skris} 11765668Skris 11892555Sdesstatic Attrib * 11965668Skrisget_attrib(void) 12065668Skris{ 12165668Skris return decode_attrib(&iqueue); 12265668Skris} 12365668Skris 12465668Skris/* handle handles */ 12565668Skris 12665668Skristypedef struct Handle Handle; 12765668Skrisstruct Handle { 12865668Skris int use; 12965668Skris DIR *dirp; 13065668Skris int fd; 13165668Skris char *name; 13265668Skris}; 13376259Sgreen 13465668Skrisenum { 13565668Skris HANDLE_UNUSED, 13665668Skris HANDLE_DIR, 13765668Skris HANDLE_FILE 13865668Skris}; 13976259Sgreen 14065668SkrisHandle handles[100]; 14165668Skris 14292555Sdesstatic void 14365668Skrishandle_init(void) 14465668Skris{ 14565668Skris int i; 14676259Sgreen 14792555Sdes for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) 14865668Skris handles[i].use = HANDLE_UNUSED; 14965668Skris} 15065668Skris 15192555Sdesstatic int 15265668Skrishandle_new(int use, char *name, int fd, DIR *dirp) 15365668Skris{ 15465668Skris int i; 15576259Sgreen 15692555Sdes for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) { 15765668Skris if (handles[i].use == HANDLE_UNUSED) { 15865668Skris handles[i].use = use; 15965668Skris handles[i].dirp = dirp; 16065668Skris handles[i].fd = fd; 161113908Sdes handles[i].name = xstrdup(name); 16265668Skris return i; 16365668Skris } 16465668Skris } 16565668Skris return -1; 16665668Skris} 16765668Skris 16892555Sdesstatic int 16965668Skrishandle_is_ok(int i, int type) 17065668Skris{ 17176259Sgreen return i >= 0 && i < sizeof(handles)/sizeof(Handle) && 17276259Sgreen handles[i].use == type; 17365668Skris} 17465668Skris 17592555Sdesstatic int 17665668Skrishandle_to_string(int handle, char **stringp, int *hlenp) 17765668Skris{ 17865668Skris if (stringp == NULL || hlenp == NULL) 17965668Skris return -1; 18076259Sgreen *stringp = xmalloc(sizeof(int32_t)); 18176259Sgreen PUT_32BIT(*stringp, handle); 18276259Sgreen *hlenp = sizeof(int32_t); 18365668Skris return 0; 18465668Skris} 18565668Skris 18692555Sdesstatic int 18765668Skrishandle_from_string(char *handle, u_int hlen) 18865668Skris{ 18976259Sgreen int val; 19076259Sgreen 19176259Sgreen if (hlen != sizeof(int32_t)) 19265668Skris return -1; 19376259Sgreen val = GET_32BIT(handle); 19465668Skris if (handle_is_ok(val, HANDLE_FILE) || 19565668Skris handle_is_ok(val, HANDLE_DIR)) 19665668Skris return val; 19765668Skris return -1; 19865668Skris} 19965668Skris 20092555Sdesstatic char * 20165668Skrishandle_to_name(int handle) 20265668Skris{ 20365668Skris if (handle_is_ok(handle, HANDLE_DIR)|| 20465668Skris handle_is_ok(handle, HANDLE_FILE)) 20565668Skris return handles[handle].name; 20665668Skris return NULL; 20765668Skris} 20865668Skris 20992555Sdesstatic DIR * 21065668Skrishandle_to_dir(int handle) 21165668Skris{ 21265668Skris if (handle_is_ok(handle, HANDLE_DIR)) 21365668Skris return handles[handle].dirp; 21465668Skris return NULL; 21565668Skris} 21665668Skris 21792555Sdesstatic int 21865668Skrishandle_to_fd(int handle) 21965668Skris{ 22076259Sgreen if (handle_is_ok(handle, HANDLE_FILE)) 22165668Skris return handles[handle].fd; 22265668Skris return -1; 22365668Skris} 22465668Skris 22592555Sdesstatic int 22665668Skrishandle_close(int handle) 22765668Skris{ 22865668Skris int ret = -1; 22976259Sgreen 23065668Skris if (handle_is_ok(handle, HANDLE_FILE)) { 23165668Skris ret = close(handles[handle].fd); 23265668Skris handles[handle].use = HANDLE_UNUSED; 233113908Sdes xfree(handles[handle].name); 23465668Skris } else if (handle_is_ok(handle, HANDLE_DIR)) { 23565668Skris ret = closedir(handles[handle].dirp); 23665668Skris handles[handle].use = HANDLE_UNUSED; 237113908Sdes xfree(handles[handle].name); 23865668Skris } else { 23965668Skris errno = ENOENT; 24065668Skris } 24165668Skris return ret; 24265668Skris} 24365668Skris 24492555Sdesstatic int 24565668Skrisget_handle(void) 24665668Skris{ 24765668Skris char *handle; 24876259Sgreen int val = -1; 24965668Skris u_int hlen; 25076259Sgreen 25165668Skris handle = get_string(&hlen); 25276259Sgreen if (hlen < 256) 25376259Sgreen val = handle_from_string(handle, hlen); 25465668Skris xfree(handle); 25565668Skris return val; 25665668Skris} 25765668Skris 25865668Skris/* send replies */ 25965668Skris 26092555Sdesstatic void 26165668Skrissend_msg(Buffer *m) 26265668Skris{ 26365668Skris int mlen = buffer_len(m); 26476259Sgreen 26565668Skris buffer_put_int(&oqueue, mlen); 26665668Skris buffer_append(&oqueue, buffer_ptr(m), mlen); 26765668Skris buffer_consume(m, mlen); 26865668Skris} 26965668Skris 27092555Sdesstatic void 27165668Skrissend_status(u_int32_t id, u_int32_t error) 27265668Skris{ 27365668Skris Buffer msg; 27476259Sgreen const char *status_messages[] = { 27576259Sgreen "Success", /* SSH_FX_OK */ 27676259Sgreen "End of file", /* SSH_FX_EOF */ 27776259Sgreen "No such file", /* SSH_FX_NO_SUCH_FILE */ 27876259Sgreen "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 27976259Sgreen "Failure", /* SSH_FX_FAILURE */ 28076259Sgreen "Bad message", /* SSH_FX_BAD_MESSAGE */ 28176259Sgreen "No connection", /* SSH_FX_NO_CONNECTION */ 28276259Sgreen "Connection lost", /* SSH_FX_CONNECTION_LOST */ 28376259Sgreen "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 28476259Sgreen "Unknown error" /* Others */ 28576259Sgreen }; 28676259Sgreen 28799060Sdes TRACE("sent status id %u error %u", id, error); 28865668Skris buffer_init(&msg); 28976259Sgreen buffer_put_char(&msg, SSH2_FXP_STATUS); 29065668Skris buffer_put_int(&msg, id); 29165668Skris buffer_put_int(&msg, error); 29276259Sgreen if (version >= 3) { 29376259Sgreen buffer_put_cstring(&msg, 29476259Sgreen status_messages[MIN(error,SSH2_FX_MAX)]); 29576259Sgreen buffer_put_cstring(&msg, ""); 29676259Sgreen } 29765668Skris send_msg(&msg); 29865668Skris buffer_free(&msg); 29965668Skris} 30092555Sdesstatic void 30165668Skrissend_data_or_handle(char type, u_int32_t id, char *data, int dlen) 30265668Skris{ 30365668Skris Buffer msg; 30476259Sgreen 30565668Skris buffer_init(&msg); 30665668Skris buffer_put_char(&msg, type); 30765668Skris buffer_put_int(&msg, id); 30865668Skris buffer_put_string(&msg, data, dlen); 30965668Skris send_msg(&msg); 31065668Skris buffer_free(&msg); 31165668Skris} 31265668Skris 31392555Sdesstatic void 31465668Skrissend_data(u_int32_t id, char *data, int dlen) 31565668Skris{ 31699060Sdes TRACE("sent data id %u len %d", id, dlen); 31776259Sgreen send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 31865668Skris} 31965668Skris 32092555Sdesstatic void 32165668Skrissend_handle(u_int32_t id, int handle) 32265668Skris{ 32365668Skris char *string; 32465668Skris int hlen; 32576259Sgreen 32665668Skris handle_to_string(handle, &string, &hlen); 32799060Sdes TRACE("sent handle id %u handle %d", id, handle); 32876259Sgreen send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 32965668Skris xfree(string); 33065668Skris} 33165668Skris 33292555Sdesstatic void 33365668Skrissend_names(u_int32_t id, int count, Stat *stats) 33465668Skris{ 33565668Skris Buffer msg; 33665668Skris int i; 33776259Sgreen 33865668Skris buffer_init(&msg); 33976259Sgreen buffer_put_char(&msg, SSH2_FXP_NAME); 34065668Skris buffer_put_int(&msg, id); 34165668Skris buffer_put_int(&msg, count); 34299060Sdes TRACE("sent names id %u count %d", id, count); 34365668Skris for (i = 0; i < count; i++) { 34465668Skris buffer_put_cstring(&msg, stats[i].name); 34565668Skris buffer_put_cstring(&msg, stats[i].long_name); 34665668Skris encode_attrib(&msg, &stats[i].attrib); 34765668Skris } 34865668Skris send_msg(&msg); 34965668Skris buffer_free(&msg); 35065668Skris} 35165668Skris 35292555Sdesstatic void 35365668Skrissend_attrib(u_int32_t id, Attrib *a) 35465668Skris{ 35565668Skris Buffer msg; 35676259Sgreen 35799060Sdes TRACE("sent attrib id %u have 0x%x", id, a->flags); 35865668Skris buffer_init(&msg); 35976259Sgreen buffer_put_char(&msg, SSH2_FXP_ATTRS); 36065668Skris buffer_put_int(&msg, id); 36165668Skris encode_attrib(&msg, a); 36265668Skris send_msg(&msg); 36365668Skris buffer_free(&msg); 36465668Skris} 36565668Skris 36665668Skris/* parse incoming */ 36765668Skris 36892555Sdesstatic void 36965668Skrisprocess_init(void) 37065668Skris{ 37165668Skris Buffer msg; 37265668Skris 37398675Sdes version = get_int(); 37465668Skris TRACE("client version %d", version); 37565668Skris buffer_init(&msg); 37676259Sgreen buffer_put_char(&msg, SSH2_FXP_VERSION); 37776259Sgreen buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 37865668Skris send_msg(&msg); 37965668Skris buffer_free(&msg); 38065668Skris} 38165668Skris 38292555Sdesstatic void 38365668Skrisprocess_open(void) 38465668Skris{ 38565668Skris u_int32_t id, pflags; 38665668Skris Attrib *a; 38765668Skris char *name; 38876259Sgreen int handle, fd, flags, mode, status = SSH2_FX_FAILURE; 38965668Skris 39065668Skris id = get_int(); 39165668Skris name = get_string(NULL); 39276259Sgreen pflags = get_int(); /* portable flags */ 39365668Skris a = get_attrib(); 39465668Skris flags = flags_from_portable(pflags); 39576259Sgreen mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; 39699060Sdes TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode); 39765668Skris fd = open(name, flags, mode); 39865668Skris if (fd < 0) { 39965668Skris status = errno_to_portable(errno); 40065668Skris } else { 401113908Sdes handle = handle_new(HANDLE_FILE, name, fd, NULL); 40265668Skris if (handle < 0) { 40365668Skris close(fd); 40465668Skris } else { 40565668Skris send_handle(id, handle); 40676259Sgreen status = SSH2_FX_OK; 40765668Skris } 40865668Skris } 40976259Sgreen if (status != SSH2_FX_OK) 41065668Skris send_status(id, status); 41165668Skris xfree(name); 41265668Skris} 41365668Skris 41492555Sdesstatic void 41565668Skrisprocess_close(void) 41665668Skris{ 41765668Skris u_int32_t id; 41876259Sgreen int handle, ret, status = SSH2_FX_FAILURE; 41965668Skris 42065668Skris id = get_int(); 42165668Skris handle = get_handle(); 42299060Sdes TRACE("close id %u handle %d", id, handle); 42365668Skris ret = handle_close(handle); 42476259Sgreen status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 42565668Skris send_status(id, status); 42665668Skris} 42765668Skris 42892555Sdesstatic void 42965668Skrisprocess_read(void) 43065668Skris{ 43165668Skris char buf[64*1024]; 43276259Sgreen u_int32_t id, len; 43376259Sgreen int handle, fd, ret, status = SSH2_FX_FAILURE; 43465668Skris u_int64_t off; 43565668Skris 43665668Skris id = get_int(); 43765668Skris handle = get_handle(); 43876259Sgreen off = get_int64(); 43965668Skris len = get_int(); 44065668Skris 44199060Sdes TRACE("read id %u handle %d off %llu len %d", id, handle, 44298937Sdes (u_int64_t)off, len); 44365668Skris if (len > sizeof buf) { 44465668Skris len = sizeof buf; 445124208Sdes logit("read change len %d", len); 44665668Skris } 44765668Skris fd = handle_to_fd(handle); 44865668Skris if (fd >= 0) { 44965668Skris if (lseek(fd, off, SEEK_SET) < 0) { 45065668Skris error("process_read: seek failed"); 45165668Skris status = errno_to_portable(errno); 45265668Skris } else { 45365668Skris ret = read(fd, buf, len); 45465668Skris if (ret < 0) { 45565668Skris status = errno_to_portable(errno); 45665668Skris } else if (ret == 0) { 45776259Sgreen status = SSH2_FX_EOF; 45865668Skris } else { 45965668Skris send_data(id, buf, ret); 46076259Sgreen status = SSH2_FX_OK; 46165668Skris } 46265668Skris } 46365668Skris } 46476259Sgreen if (status != SSH2_FX_OK) 46565668Skris send_status(id, status); 46665668Skris} 46765668Skris 46892555Sdesstatic void 46965668Skrisprocess_write(void) 47065668Skris{ 47176259Sgreen u_int32_t id; 47265668Skris u_int64_t off; 47365668Skris u_int len; 47476259Sgreen int handle, fd, ret, status = SSH2_FX_FAILURE; 47565668Skris char *data; 47665668Skris 47765668Skris id = get_int(); 47865668Skris handle = get_handle(); 47976259Sgreen off = get_int64(); 48065668Skris data = get_string(&len); 48165668Skris 48299060Sdes TRACE("write id %u handle %d off %llu len %d", id, handle, 48398937Sdes (u_int64_t)off, len); 48465668Skris fd = handle_to_fd(handle); 48565668Skris if (fd >= 0) { 48665668Skris if (lseek(fd, off, SEEK_SET) < 0) { 48765668Skris status = errno_to_portable(errno); 48865668Skris error("process_write: seek failed"); 48965668Skris } else { 49065668Skris/* XXX ATOMICIO ? */ 49165668Skris ret = write(fd, data, len); 49265668Skris if (ret == -1) { 49365668Skris error("process_write: write failed"); 49465668Skris status = errno_to_portable(errno); 49565668Skris } else if (ret == len) { 49676259Sgreen status = SSH2_FX_OK; 49765668Skris } else { 498124208Sdes logit("nothing at all written"); 49965668Skris } 50065668Skris } 50165668Skris } 50265668Skris send_status(id, status); 50365668Skris xfree(data); 50465668Skris} 50565668Skris 50692555Sdesstatic void 50765668Skrisprocess_do_stat(int do_lstat) 50865668Skris{ 50976259Sgreen Attrib a; 51065668Skris struct stat st; 51165668Skris u_int32_t id; 51265668Skris char *name; 51376259Sgreen int ret, status = SSH2_FX_FAILURE; 51465668Skris 51565668Skris id = get_int(); 51665668Skris name = get_string(NULL); 51799060Sdes TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name); 51865668Skris ret = do_lstat ? lstat(name, &st) : stat(name, &st); 51965668Skris if (ret < 0) { 52065668Skris status = errno_to_portable(errno); 52165668Skris } else { 52276259Sgreen stat_to_attrib(&st, &a); 52376259Sgreen send_attrib(id, &a); 52476259Sgreen status = SSH2_FX_OK; 52565668Skris } 52676259Sgreen if (status != SSH2_FX_OK) 52765668Skris send_status(id, status); 52865668Skris xfree(name); 52965668Skris} 53065668Skris 53192555Sdesstatic void 53265668Skrisprocess_stat(void) 53365668Skris{ 53465668Skris process_do_stat(0); 53565668Skris} 53665668Skris 53792555Sdesstatic void 53865668Skrisprocess_lstat(void) 53965668Skris{ 54065668Skris process_do_stat(1); 54165668Skris} 54265668Skris 54392555Sdesstatic void 54465668Skrisprocess_fstat(void) 54565668Skris{ 54676259Sgreen Attrib a; 54765668Skris struct stat st; 54865668Skris u_int32_t id; 54976259Sgreen int fd, ret, handle, status = SSH2_FX_FAILURE; 55065668Skris 55165668Skris id = get_int(); 55265668Skris handle = get_handle(); 55399060Sdes TRACE("fstat id %u handle %d", id, handle); 55465668Skris fd = handle_to_fd(handle); 55565668Skris if (fd >= 0) { 55665668Skris ret = fstat(fd, &st); 55765668Skris if (ret < 0) { 55865668Skris status = errno_to_portable(errno); 55965668Skris } else { 56076259Sgreen stat_to_attrib(&st, &a); 56176259Sgreen send_attrib(id, &a); 56276259Sgreen status = SSH2_FX_OK; 56365668Skris } 56465668Skris } 56576259Sgreen if (status != SSH2_FX_OK) 56665668Skris send_status(id, status); 56765668Skris} 56865668Skris 56992555Sdesstatic struct timeval * 57065668Skrisattrib_to_tv(Attrib *a) 57165668Skris{ 57265668Skris static struct timeval tv[2]; 57376259Sgreen 57465668Skris tv[0].tv_sec = a->atime; 57565668Skris tv[0].tv_usec = 0; 57665668Skris tv[1].tv_sec = a->mtime; 57765668Skris tv[1].tv_usec = 0; 57865668Skris return tv; 57965668Skris} 58065668Skris 58192555Sdesstatic void 58265668Skrisprocess_setstat(void) 58365668Skris{ 58465668Skris Attrib *a; 58565668Skris u_int32_t id; 58665668Skris char *name; 58799060Sdes int status = SSH2_FX_OK, ret; 58865668Skris 58965668Skris id = get_int(); 59065668Skris name = get_string(NULL); 59165668Skris a = get_attrib(); 59299060Sdes TRACE("setstat id %u name %s", id, name); 59392555Sdes if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 59492555Sdes ret = truncate(name, a->size); 59592555Sdes if (ret == -1) 59692555Sdes status = errno_to_portable(errno); 59792555Sdes } 59876259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 59965668Skris ret = chmod(name, a->perm & 0777); 60065668Skris if (ret == -1) 60165668Skris status = errno_to_portable(errno); 60265668Skris } 60376259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 60465668Skris ret = utimes(name, attrib_to_tv(a)); 60565668Skris if (ret == -1) 60665668Skris status = errno_to_portable(errno); 60765668Skris } 60876259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 60976259Sgreen ret = chown(name, a->uid, a->gid); 61076259Sgreen if (ret == -1) 61176259Sgreen status = errno_to_portable(errno); 61276259Sgreen } 61365668Skris send_status(id, status); 61465668Skris xfree(name); 61565668Skris} 61665668Skris 61792555Sdesstatic void 61865668Skrisprocess_fsetstat(void) 61965668Skris{ 62065668Skris Attrib *a; 62165668Skris u_int32_t id; 62265668Skris int handle, fd, ret; 62376259Sgreen int status = SSH2_FX_OK; 62498937Sdes char *name; 62565668Skris 62665668Skris id = get_int(); 62765668Skris handle = get_handle(); 62865668Skris a = get_attrib(); 62999060Sdes TRACE("fsetstat id %u handle %d", id, handle); 63065668Skris fd = handle_to_fd(handle); 63198937Sdes name = handle_to_name(handle); 63298937Sdes if (fd < 0 || name == NULL) { 63376259Sgreen status = SSH2_FX_FAILURE; 63465668Skris } else { 63592555Sdes if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 63692555Sdes ret = ftruncate(fd, a->size); 63792555Sdes if (ret == -1) 63892555Sdes status = errno_to_portable(errno); 63992555Sdes } 64076259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 64198937Sdes#ifdef HAVE_FCHMOD 64265668Skris ret = fchmod(fd, a->perm & 0777); 64398937Sdes#else 64498937Sdes ret = chmod(name, a->perm & 0777); 64598937Sdes#endif 64665668Skris if (ret == -1) 64765668Skris status = errno_to_portable(errno); 64865668Skris } 64976259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 65098937Sdes#ifdef HAVE_FUTIMES 65165668Skris ret = futimes(fd, attrib_to_tv(a)); 65298937Sdes#else 65398937Sdes ret = utimes(name, attrib_to_tv(a)); 65498937Sdes#endif 65565668Skris if (ret == -1) 65665668Skris status = errno_to_portable(errno); 65765668Skris } 65876259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 65998937Sdes#ifdef HAVE_FCHOWN 66076259Sgreen ret = fchown(fd, a->uid, a->gid); 66198937Sdes#else 66298937Sdes ret = chown(name, a->uid, a->gid); 66398937Sdes#endif 66476259Sgreen if (ret == -1) 66576259Sgreen status = errno_to_portable(errno); 66676259Sgreen } 66765668Skris } 66865668Skris send_status(id, status); 66965668Skris} 67065668Skris 67192555Sdesstatic void 67265668Skrisprocess_opendir(void) 67365668Skris{ 67465668Skris DIR *dirp = NULL; 67565668Skris char *path; 67676259Sgreen int handle, status = SSH2_FX_FAILURE; 67765668Skris u_int32_t id; 67865668Skris 67965668Skris id = get_int(); 68065668Skris path = get_string(NULL); 68199060Sdes TRACE("opendir id %u path %s", id, path); 68276259Sgreen dirp = opendir(path); 68365668Skris if (dirp == NULL) { 68465668Skris status = errno_to_portable(errno); 68565668Skris } else { 686113908Sdes handle = handle_new(HANDLE_DIR, path, 0, dirp); 68765668Skris if (handle < 0) { 68865668Skris closedir(dirp); 68965668Skris } else { 69065668Skris send_handle(id, handle); 69176259Sgreen status = SSH2_FX_OK; 69265668Skris } 69376259Sgreen 69465668Skris } 69576259Sgreen if (status != SSH2_FX_OK) 69665668Skris send_status(id, status); 69765668Skris xfree(path); 69865668Skris} 69965668Skris 70092555Sdesstatic void 70165668Skrisprocess_readdir(void) 70265668Skris{ 70365668Skris DIR *dirp; 70465668Skris struct dirent *dp; 70565668Skris char *path; 70665668Skris int handle; 70765668Skris u_int32_t id; 70865668Skris 70965668Skris id = get_int(); 71065668Skris handle = get_handle(); 71199060Sdes TRACE("readdir id %u handle %d", id, handle); 71265668Skris dirp = handle_to_dir(handle); 71365668Skris path = handle_to_name(handle); 71465668Skris if (dirp == NULL || path == NULL) { 71576259Sgreen send_status(id, SSH2_FX_FAILURE); 71665668Skris } else { 71765668Skris struct stat st; 71865668Skris char pathname[1024]; 71965668Skris Stat *stats; 72065668Skris int nstats = 10, count = 0, i; 72199060Sdes 72265668Skris stats = xmalloc(nstats * sizeof(Stat)); 72365668Skris while ((dp = readdir(dirp)) != NULL) { 72465668Skris if (count >= nstats) { 72565668Skris nstats *= 2; 72665668Skris stats = xrealloc(stats, nstats * sizeof(Stat)); 72765668Skris } 72865668Skris/* XXX OVERFLOW ? */ 72992555Sdes snprintf(pathname, sizeof pathname, "%s%s%s", path, 73092555Sdes strcmp(path, "/") ? "/" : "", dp->d_name); 73165668Skris if (lstat(pathname, &st) < 0) 73265668Skris continue; 73376259Sgreen stat_to_attrib(&st, &(stats[count].attrib)); 73465668Skris stats[count].name = xstrdup(dp->d_name); 735106121Sdes stats[count].long_name = ls_file(dp->d_name, &st, 0); 73665668Skris count++; 73765668Skris /* send up to 100 entries in one message */ 73876259Sgreen /* XXX check packet size instead */ 73965668Skris if (count == 100) 74065668Skris break; 74165668Skris } 74276259Sgreen if (count > 0) { 74376259Sgreen send_names(id, count, stats); 74492555Sdes for (i = 0; i < count; i++) { 74576259Sgreen xfree(stats[i].name); 74676259Sgreen xfree(stats[i].long_name); 74776259Sgreen } 74876259Sgreen } else { 74976259Sgreen send_status(id, SSH2_FX_EOF); 75065668Skris } 75165668Skris xfree(stats); 75265668Skris } 75365668Skris} 75465668Skris 75592555Sdesstatic void 75665668Skrisprocess_remove(void) 75765668Skris{ 75865668Skris char *name; 75965668Skris u_int32_t id; 76076259Sgreen int status = SSH2_FX_FAILURE; 76165668Skris int ret; 76265668Skris 76365668Skris id = get_int(); 76465668Skris name = get_string(NULL); 76599060Sdes TRACE("remove id %u name %s", id, name); 76676259Sgreen ret = unlink(name); 76776259Sgreen status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 76865668Skris send_status(id, status); 76965668Skris xfree(name); 77065668Skris} 77165668Skris 77292555Sdesstatic void 77365668Skrisprocess_mkdir(void) 77465668Skris{ 77565668Skris Attrib *a; 77665668Skris u_int32_t id; 77765668Skris char *name; 77876259Sgreen int ret, mode, status = SSH2_FX_FAILURE; 77965668Skris 78065668Skris id = get_int(); 78165668Skris name = get_string(NULL); 78265668Skris a = get_attrib(); 78376259Sgreen mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 78476259Sgreen a->perm & 0777 : 0777; 78599060Sdes TRACE("mkdir id %u name %s mode 0%o", id, name, mode); 78665668Skris ret = mkdir(name, mode); 78776259Sgreen status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 78865668Skris send_status(id, status); 78965668Skris xfree(name); 79065668Skris} 79165668Skris 79292555Sdesstatic void 79365668Skrisprocess_rmdir(void) 79465668Skris{ 79565668Skris u_int32_t id; 79665668Skris char *name; 79765668Skris int ret, status; 79865668Skris 79965668Skris id = get_int(); 80065668Skris name = get_string(NULL); 80199060Sdes TRACE("rmdir id %u name %s", id, name); 80265668Skris ret = rmdir(name); 80376259Sgreen status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 80465668Skris send_status(id, status); 80565668Skris xfree(name); 80665668Skris} 80765668Skris 80892555Sdesstatic void 80965668Skrisprocess_realpath(void) 81065668Skris{ 81165668Skris char resolvedname[MAXPATHLEN]; 81265668Skris u_int32_t id; 81365668Skris char *path; 81465668Skris 81565668Skris id = get_int(); 81665668Skris path = get_string(NULL); 81776259Sgreen if (path[0] == '\0') { 81876259Sgreen xfree(path); 81976259Sgreen path = xstrdup("."); 82076259Sgreen } 82199060Sdes TRACE("realpath id %u path %s", id, path); 82265668Skris if (realpath(path, resolvedname) == NULL) { 82365668Skris send_status(id, errno_to_portable(errno)); 82465668Skris } else { 82565668Skris Stat s; 82665668Skris attrib_clear(&s.attrib); 82765668Skris s.name = s.long_name = resolvedname; 82865668Skris send_names(id, 1, &s); 82965668Skris } 83065668Skris xfree(path); 83165668Skris} 83265668Skris 83392555Sdesstatic void 83465668Skrisprocess_rename(void) 83565668Skris{ 83665668Skris u_int32_t id; 83765668Skris char *oldpath, *newpath; 838113908Sdes int status; 839113908Sdes struct stat sb; 84065668Skris 84165668Skris id = get_int(); 84265668Skris oldpath = get_string(NULL); 84365668Skris newpath = get_string(NULL); 84499060Sdes TRACE("rename id %u old %s new %s", id, oldpath, newpath); 845113908Sdes status = SSH2_FX_FAILURE; 846113908Sdes if (lstat(oldpath, &sb) == -1) 847113908Sdes status = errno_to_portable(errno); 848113908Sdes else if (S_ISREG(sb.st_mode)) { 849113908Sdes /* Race-free rename of regular files */ 850113908Sdes if (link(oldpath, newpath) == -1) 851113908Sdes status = errno_to_portable(errno); 852113908Sdes else if (unlink(oldpath) == -1) { 853113908Sdes status = errno_to_portable(errno); 854113908Sdes /* clean spare link */ 855113908Sdes unlink(newpath); 856113908Sdes } else 857113908Sdes status = SSH2_FX_OK; 858113908Sdes } else if (stat(newpath, &sb) == -1) { 859113908Sdes if (rename(oldpath, newpath) == -1) 860113908Sdes status = errno_to_portable(errno); 861113908Sdes else 862113908Sdes status = SSH2_FX_OK; 86376259Sgreen } 86465668Skris send_status(id, status); 86565668Skris xfree(oldpath); 86665668Skris xfree(newpath); 86765668Skris} 86865668Skris 86992555Sdesstatic void 87076259Sgreenprocess_readlink(void) 87176259Sgreen{ 87276259Sgreen u_int32_t id; 87392555Sdes int len; 87476259Sgreen char link[MAXPATHLEN]; 87576259Sgreen char *path; 87665668Skris 87776259Sgreen id = get_int(); 87876259Sgreen path = get_string(NULL); 87999060Sdes TRACE("readlink id %u path %s", id, path); 88092555Sdes if ((len = readlink(path, link, sizeof(link) - 1)) == -1) 88176259Sgreen send_status(id, errno_to_portable(errno)); 88276259Sgreen else { 88376259Sgreen Stat s; 88492555Sdes 88592555Sdes link[len] = '\0'; 88676259Sgreen attrib_clear(&s.attrib); 88776259Sgreen s.name = s.long_name = link; 88876259Sgreen send_names(id, 1, &s); 88976259Sgreen } 89076259Sgreen xfree(path); 89176259Sgreen} 89276259Sgreen 89392555Sdesstatic void 89476259Sgreenprocess_symlink(void) 89576259Sgreen{ 89676259Sgreen u_int32_t id; 89776259Sgreen char *oldpath, *newpath; 898113908Sdes int ret, status; 89976259Sgreen 90076259Sgreen id = get_int(); 90176259Sgreen oldpath = get_string(NULL); 90276259Sgreen newpath = get_string(NULL); 90399060Sdes TRACE("symlink id %u old %s new %s", id, oldpath, newpath); 904113908Sdes /* this will fail if 'newpath' exists */ 905113908Sdes ret = symlink(oldpath, newpath); 906113908Sdes status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 90776259Sgreen send_status(id, status); 90876259Sgreen xfree(oldpath); 90976259Sgreen xfree(newpath); 91076259Sgreen} 91176259Sgreen 91292555Sdesstatic void 91376259Sgreenprocess_extended(void) 91476259Sgreen{ 91576259Sgreen u_int32_t id; 91676259Sgreen char *request; 91776259Sgreen 91876259Sgreen id = get_int(); 91976259Sgreen request = get_string(NULL); 92076259Sgreen send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 92176259Sgreen xfree(request); 92276259Sgreen} 92376259Sgreen 92465668Skris/* stolen from ssh-agent */ 92565668Skris 92692555Sdesstatic void 92765668Skrisprocess(void) 92865668Skris{ 92976259Sgreen u_int msg_len; 93098675Sdes u_int buf_len; 93198675Sdes u_int consumed; 93276259Sgreen u_int type; 93376259Sgreen u_char *cp; 93465668Skris 93598675Sdes buf_len = buffer_len(&iqueue); 93698675Sdes if (buf_len < 5) 93765668Skris return; /* Incomplete message. */ 93892555Sdes cp = buffer_ptr(&iqueue); 93965668Skris msg_len = GET_32BIT(cp); 94065668Skris if (msg_len > 256 * 1024) { 94165668Skris error("bad message "); 94265668Skris exit(11); 94365668Skris } 94498675Sdes if (buf_len < msg_len + 4) 94565668Skris return; 94665668Skris buffer_consume(&iqueue, 4); 94798675Sdes buf_len -= 4; 94865668Skris type = buffer_get_char(&iqueue); 94965668Skris switch (type) { 95076259Sgreen case SSH2_FXP_INIT: 95165668Skris process_init(); 95265668Skris break; 95376259Sgreen case SSH2_FXP_OPEN: 95465668Skris process_open(); 95565668Skris break; 95676259Sgreen case SSH2_FXP_CLOSE: 95765668Skris process_close(); 95865668Skris break; 95976259Sgreen case SSH2_FXP_READ: 96065668Skris process_read(); 96165668Skris break; 96276259Sgreen case SSH2_FXP_WRITE: 96365668Skris process_write(); 96465668Skris break; 96576259Sgreen case SSH2_FXP_LSTAT: 96665668Skris process_lstat(); 96765668Skris break; 96876259Sgreen case SSH2_FXP_FSTAT: 96965668Skris process_fstat(); 97065668Skris break; 97176259Sgreen case SSH2_FXP_SETSTAT: 97265668Skris process_setstat(); 97365668Skris break; 97476259Sgreen case SSH2_FXP_FSETSTAT: 97565668Skris process_fsetstat(); 97665668Skris break; 97776259Sgreen case SSH2_FXP_OPENDIR: 97865668Skris process_opendir(); 97965668Skris break; 98076259Sgreen case SSH2_FXP_READDIR: 98165668Skris process_readdir(); 98265668Skris break; 98376259Sgreen case SSH2_FXP_REMOVE: 98465668Skris process_remove(); 98565668Skris break; 98676259Sgreen case SSH2_FXP_MKDIR: 98765668Skris process_mkdir(); 98865668Skris break; 98976259Sgreen case SSH2_FXP_RMDIR: 99065668Skris process_rmdir(); 99165668Skris break; 99276259Sgreen case SSH2_FXP_REALPATH: 99365668Skris process_realpath(); 99465668Skris break; 99576259Sgreen case SSH2_FXP_STAT: 99665668Skris process_stat(); 99765668Skris break; 99876259Sgreen case SSH2_FXP_RENAME: 99965668Skris process_rename(); 100065668Skris break; 100176259Sgreen case SSH2_FXP_READLINK: 100276259Sgreen process_readlink(); 100376259Sgreen break; 100476259Sgreen case SSH2_FXP_SYMLINK: 100576259Sgreen process_symlink(); 100676259Sgreen break; 100776259Sgreen case SSH2_FXP_EXTENDED: 100876259Sgreen process_extended(); 100976259Sgreen break; 101065668Skris default: 101165668Skris error("Unknown message %d", type); 101265668Skris break; 101365668Skris } 101498675Sdes /* discard the remaining bytes from the current packet */ 101598675Sdes if (buf_len < buffer_len(&iqueue)) 101698675Sdes fatal("iqueue grows"); 101798675Sdes consumed = buf_len - buffer_len(&iqueue); 101898675Sdes if (msg_len < consumed) 101998675Sdes fatal("msg_len %d < consumed %d", msg_len, consumed); 102098675Sdes if (msg_len > consumed) 102198675Sdes buffer_consume(&iqueue, msg_len - consumed); 102265668Skris} 102365668Skris 102465668Skrisint 102565668Skrismain(int ac, char **av) 102665668Skris{ 102776259Sgreen fd_set *rset, *wset; 102865668Skris int in, out, max; 102976259Sgreen ssize_t len, olen, set_size; 103065668Skris 103176259Sgreen /* XXX should use getopt */ 103276259Sgreen 1033124208Sdes __progname = ssh_get_progname(av[0]); 103465668Skris handle_init(); 103565668Skris 103676259Sgreen#ifdef DEBUG_SFTP_SERVER 103776259Sgreen log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); 103876259Sgreen#endif 103976259Sgreen 104065668Skris in = dup(STDIN_FILENO); 104165668Skris out = dup(STDOUT_FILENO); 104265668Skris 104398937Sdes#ifdef HAVE_CYGWIN 104498937Sdes setmode(in, O_BINARY); 104598937Sdes setmode(out, O_BINARY); 104698937Sdes#endif 104798937Sdes 104865668Skris max = 0; 104965668Skris if (in > max) 105065668Skris max = in; 105165668Skris if (out > max) 105265668Skris max = out; 105365668Skris 105465668Skris buffer_init(&iqueue); 105565668Skris buffer_init(&oqueue); 105665668Skris 105776259Sgreen set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 105876259Sgreen rset = (fd_set *)xmalloc(set_size); 105976259Sgreen wset = (fd_set *)xmalloc(set_size); 106076259Sgreen 106165668Skris for (;;) { 106276259Sgreen memset(rset, 0, set_size); 106376259Sgreen memset(wset, 0, set_size); 106465668Skris 106576259Sgreen FD_SET(in, rset); 106665668Skris olen = buffer_len(&oqueue); 106765668Skris if (olen > 0) 106876259Sgreen FD_SET(out, wset); 106965668Skris 107076259Sgreen if (select(max+1, rset, wset, NULL, NULL) < 0) { 107165668Skris if (errno == EINTR) 107265668Skris continue; 107365668Skris exit(2); 107465668Skris } 107565668Skris 107665668Skris /* copy stdin to iqueue */ 107776259Sgreen if (FD_ISSET(in, rset)) { 107865668Skris char buf[4*4096]; 107965668Skris len = read(in, buf, sizeof buf); 108065668Skris if (len == 0) { 108165668Skris debug("read eof"); 108265668Skris exit(0); 108365668Skris } else if (len < 0) { 108465668Skris error("read error"); 108565668Skris exit(1); 108665668Skris } else { 108765668Skris buffer_append(&iqueue, buf, len); 108865668Skris } 108965668Skris } 109065668Skris /* send oqueue to stdout */ 109176259Sgreen if (FD_ISSET(out, wset)) { 109265668Skris len = write(out, buffer_ptr(&oqueue), olen); 109365668Skris if (len < 0) { 109465668Skris error("write error"); 109565668Skris exit(1); 109665668Skris } else { 109765668Skris buffer_consume(&oqueue, len); 109865668Skris } 109965668Skris } 110065668Skris /* process requests from client */ 110165668Skris process(); 110265668Skris } 110365668Skris} 1104