sftp-server.c revision 126274
165668Skris/* 2126274Sdes * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. 365668Skris * 4126274Sdes * Permission to use, copy, modify, and distribute this software for any 5126274Sdes * purpose with or without fee is hereby granted, provided that the above 6126274Sdes * copyright notice and this permission notice appear in all copies. 765668Skris * 8126274Sdes * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9126274Sdes * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10126274Sdes * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11126274Sdes * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12126274Sdes * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13126274Sdes * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14126274Sdes * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1565668Skris */ 1665668Skris#include "includes.h" 17126274SdesRCSID("$OpenBSD: sftp-server.c,v 1.45 2004/02/19 21:15:04 markus Exp $"); 1865668Skris 1965668Skris#include "buffer.h" 2065668Skris#include "bufaux.h" 2165668Skris#include "getput.h" 2276259Sgreen#include "log.h" 2365668Skris#include "xmalloc.h" 2465668Skris 2576259Sgreen#include "sftp.h" 2676259Sgreen#include "sftp-common.h" 2765668Skris 2865668Skris/* helper */ 2976259Sgreen#define get_int64() buffer_get_int64(&iqueue); 3065668Skris#define get_int() buffer_get_int(&iqueue); 3165668Skris#define get_string(lenp) buffer_get_string(&iqueue, lenp); 3276259Sgreen#define TRACE debug 3365668Skris 3498937Sdes#ifdef HAVE___PROGNAME 3598937Sdesextern char *__progname; 3698937Sdes#else 3798937Sdeschar *__progname; 3898937Sdes#endif 3998937Sdes 4065668Skris/* input and output queue */ 4165668SkrisBuffer iqueue; 4265668SkrisBuffer oqueue; 4365668Skris 4476259Sgreen/* Version of client */ 4576259Sgreenint version; 4676259Sgreen 47124208Sdes/* portable attributes, etc. */ 4865668Skris 4965668Skristypedef struct Stat Stat; 5065668Skris 5176259Sgreenstruct Stat { 5265668Skris char *name; 5365668Skris char *long_name; 5465668Skris Attrib attrib; 5565668Skris}; 5665668Skris 5792555Sdesstatic int 5865668Skriserrno_to_portable(int unixerrno) 5965668Skris{ 6065668Skris int ret = 0; 6176259Sgreen 6265668Skris switch (unixerrno) { 6365668Skris case 0: 6476259Sgreen ret = SSH2_FX_OK; 6565668Skris break; 6665668Skris case ENOENT: 6765668Skris case ENOTDIR: 6865668Skris case EBADF: 6965668Skris case ELOOP: 7076259Sgreen ret = SSH2_FX_NO_SUCH_FILE; 7165668Skris break; 7265668Skris case EPERM: 7365668Skris case EACCES: 7465668Skris case EFAULT: 7576259Sgreen ret = SSH2_FX_PERMISSION_DENIED; 7665668Skris break; 7765668Skris case ENAMETOOLONG: 7865668Skris case EINVAL: 7976259Sgreen ret = SSH2_FX_BAD_MESSAGE; 8065668Skris break; 8165668Skris default: 8276259Sgreen ret = SSH2_FX_FAILURE; 8365668Skris break; 8465668Skris } 8565668Skris return ret; 8665668Skris} 8765668Skris 8892555Sdesstatic int 8965668Skrisflags_from_portable(int pflags) 9065668Skris{ 9165668Skris int flags = 0; 9276259Sgreen 9376259Sgreen if ((pflags & SSH2_FXF_READ) && 9476259Sgreen (pflags & SSH2_FXF_WRITE)) { 9565668Skris flags = O_RDWR; 9676259Sgreen } else if (pflags & SSH2_FXF_READ) { 9765668Skris flags = O_RDONLY; 9876259Sgreen } else if (pflags & SSH2_FXF_WRITE) { 9965668Skris flags = O_WRONLY; 10065668Skris } 10176259Sgreen if (pflags & SSH2_FXF_CREAT) 10265668Skris flags |= O_CREAT; 10376259Sgreen if (pflags & SSH2_FXF_TRUNC) 10465668Skris flags |= O_TRUNC; 10576259Sgreen if (pflags & SSH2_FXF_EXCL) 10665668Skris flags |= O_EXCL; 10765668Skris return flags; 10865668Skris} 10965668Skris 11092555Sdesstatic Attrib * 11165668Skrisget_attrib(void) 11265668Skris{ 11365668Skris return decode_attrib(&iqueue); 11465668Skris} 11565668Skris 11665668Skris/* handle handles */ 11765668Skris 11865668Skristypedef struct Handle Handle; 11965668Skrisstruct Handle { 12065668Skris int use; 12165668Skris DIR *dirp; 12265668Skris int fd; 12365668Skris char *name; 12465668Skris}; 12576259Sgreen 12665668Skrisenum { 12765668Skris HANDLE_UNUSED, 12865668Skris HANDLE_DIR, 12965668Skris HANDLE_FILE 13065668Skris}; 13176259Sgreen 13265668SkrisHandle handles[100]; 13365668Skris 13492555Sdesstatic void 13565668Skrishandle_init(void) 13665668Skris{ 13765668Skris int i; 13876259Sgreen 13992555Sdes for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) 14065668Skris handles[i].use = HANDLE_UNUSED; 14165668Skris} 14265668Skris 14392555Sdesstatic int 144126274Sdeshandle_new(int use, const char *name, int fd, DIR *dirp) 14565668Skris{ 14665668Skris int i; 14776259Sgreen 14892555Sdes for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) { 14965668Skris if (handles[i].use == HANDLE_UNUSED) { 15065668Skris handles[i].use = use; 15165668Skris handles[i].dirp = dirp; 15265668Skris handles[i].fd = fd; 153113908Sdes handles[i].name = xstrdup(name); 15465668Skris return i; 15565668Skris } 15665668Skris } 15765668Skris return -1; 15865668Skris} 15965668Skris 16092555Sdesstatic int 16165668Skrishandle_is_ok(int i, int type) 16265668Skris{ 16376259Sgreen return i >= 0 && i < sizeof(handles)/sizeof(Handle) && 16476259Sgreen handles[i].use == type; 16565668Skris} 16665668Skris 16792555Sdesstatic int 16865668Skrishandle_to_string(int handle, char **stringp, int *hlenp) 16965668Skris{ 17065668Skris if (stringp == NULL || hlenp == NULL) 17165668Skris return -1; 17276259Sgreen *stringp = xmalloc(sizeof(int32_t)); 17376259Sgreen PUT_32BIT(*stringp, handle); 17476259Sgreen *hlenp = sizeof(int32_t); 17565668Skris return 0; 17665668Skris} 17765668Skris 17892555Sdesstatic int 179126274Sdeshandle_from_string(const char *handle, u_int hlen) 18065668Skris{ 18176259Sgreen int val; 18276259Sgreen 18376259Sgreen if (hlen != sizeof(int32_t)) 18465668Skris return -1; 18576259Sgreen val = GET_32BIT(handle); 18665668Skris if (handle_is_ok(val, HANDLE_FILE) || 18765668Skris handle_is_ok(val, HANDLE_DIR)) 18865668Skris return val; 18965668Skris return -1; 19065668Skris} 19165668Skris 19292555Sdesstatic char * 19365668Skrishandle_to_name(int handle) 19465668Skris{ 19565668Skris if (handle_is_ok(handle, HANDLE_DIR)|| 19665668Skris handle_is_ok(handle, HANDLE_FILE)) 19765668Skris return handles[handle].name; 19865668Skris return NULL; 19965668Skris} 20065668Skris 20192555Sdesstatic DIR * 20265668Skrishandle_to_dir(int handle) 20365668Skris{ 20465668Skris if (handle_is_ok(handle, HANDLE_DIR)) 20565668Skris return handles[handle].dirp; 20665668Skris return NULL; 20765668Skris} 20865668Skris 20992555Sdesstatic int 21065668Skrishandle_to_fd(int handle) 21165668Skris{ 21276259Sgreen if (handle_is_ok(handle, HANDLE_FILE)) 21365668Skris return handles[handle].fd; 21465668Skris return -1; 21565668Skris} 21665668Skris 21792555Sdesstatic int 21865668Skrishandle_close(int handle) 21965668Skris{ 22065668Skris int ret = -1; 22176259Sgreen 22265668Skris if (handle_is_ok(handle, HANDLE_FILE)) { 22365668Skris ret = close(handles[handle].fd); 22465668Skris handles[handle].use = HANDLE_UNUSED; 225113908Sdes xfree(handles[handle].name); 22665668Skris } else if (handle_is_ok(handle, HANDLE_DIR)) { 22765668Skris ret = closedir(handles[handle].dirp); 22865668Skris handles[handle].use = HANDLE_UNUSED; 229113908Sdes xfree(handles[handle].name); 23065668Skris } else { 23165668Skris errno = ENOENT; 23265668Skris } 23365668Skris return ret; 23465668Skris} 23565668Skris 23692555Sdesstatic int 23765668Skrisget_handle(void) 23865668Skris{ 23965668Skris char *handle; 24076259Sgreen int val = -1; 24165668Skris u_int hlen; 24276259Sgreen 24365668Skris handle = get_string(&hlen); 24476259Sgreen if (hlen < 256) 24576259Sgreen val = handle_from_string(handle, hlen); 24665668Skris xfree(handle); 24765668Skris return val; 24865668Skris} 24965668Skris 25065668Skris/* send replies */ 25165668Skris 25292555Sdesstatic void 25365668Skrissend_msg(Buffer *m) 25465668Skris{ 25565668Skris int mlen = buffer_len(m); 25676259Sgreen 25765668Skris buffer_put_int(&oqueue, mlen); 25865668Skris buffer_append(&oqueue, buffer_ptr(m), mlen); 25965668Skris buffer_consume(m, mlen); 26065668Skris} 26165668Skris 26292555Sdesstatic void 26365668Skrissend_status(u_int32_t id, u_int32_t error) 26465668Skris{ 26565668Skris Buffer msg; 26676259Sgreen const char *status_messages[] = { 26776259Sgreen "Success", /* SSH_FX_OK */ 26876259Sgreen "End of file", /* SSH_FX_EOF */ 26976259Sgreen "No such file", /* SSH_FX_NO_SUCH_FILE */ 27076259Sgreen "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 27176259Sgreen "Failure", /* SSH_FX_FAILURE */ 27276259Sgreen "Bad message", /* SSH_FX_BAD_MESSAGE */ 27376259Sgreen "No connection", /* SSH_FX_NO_CONNECTION */ 27476259Sgreen "Connection lost", /* SSH_FX_CONNECTION_LOST */ 27576259Sgreen "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 27676259Sgreen "Unknown error" /* Others */ 27776259Sgreen }; 27876259Sgreen 27999060Sdes TRACE("sent status id %u error %u", id, error); 28065668Skris buffer_init(&msg); 28176259Sgreen buffer_put_char(&msg, SSH2_FXP_STATUS); 28265668Skris buffer_put_int(&msg, id); 28365668Skris buffer_put_int(&msg, error); 28476259Sgreen if (version >= 3) { 28576259Sgreen buffer_put_cstring(&msg, 28676259Sgreen status_messages[MIN(error,SSH2_FX_MAX)]); 28776259Sgreen buffer_put_cstring(&msg, ""); 28876259Sgreen } 28965668Skris send_msg(&msg); 29065668Skris buffer_free(&msg); 29165668Skris} 29292555Sdesstatic void 293126274Sdessend_data_or_handle(char type, u_int32_t id, const char *data, int dlen) 29465668Skris{ 29565668Skris Buffer msg; 29676259Sgreen 29765668Skris buffer_init(&msg); 29865668Skris buffer_put_char(&msg, type); 29965668Skris buffer_put_int(&msg, id); 30065668Skris buffer_put_string(&msg, data, dlen); 30165668Skris send_msg(&msg); 30265668Skris buffer_free(&msg); 30365668Skris} 30465668Skris 30592555Sdesstatic void 306126274Sdessend_data(u_int32_t id, const char *data, int dlen) 30765668Skris{ 30899060Sdes TRACE("sent data id %u len %d", id, dlen); 30976259Sgreen send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 31065668Skris} 31165668Skris 31292555Sdesstatic void 31365668Skrissend_handle(u_int32_t id, int handle) 31465668Skris{ 31565668Skris char *string; 31665668Skris int hlen; 31776259Sgreen 31865668Skris handle_to_string(handle, &string, &hlen); 31999060Sdes TRACE("sent handle id %u handle %d", id, handle); 32076259Sgreen send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 32165668Skris xfree(string); 32265668Skris} 32365668Skris 32492555Sdesstatic void 325126274Sdessend_names(u_int32_t id, int count, const Stat *stats) 32665668Skris{ 32765668Skris Buffer msg; 32865668Skris int i; 32976259Sgreen 33065668Skris buffer_init(&msg); 33176259Sgreen buffer_put_char(&msg, SSH2_FXP_NAME); 33265668Skris buffer_put_int(&msg, id); 33365668Skris buffer_put_int(&msg, count); 33499060Sdes TRACE("sent names id %u count %d", id, count); 33565668Skris for (i = 0; i < count; i++) { 33665668Skris buffer_put_cstring(&msg, stats[i].name); 33765668Skris buffer_put_cstring(&msg, stats[i].long_name); 33865668Skris encode_attrib(&msg, &stats[i].attrib); 33965668Skris } 34065668Skris send_msg(&msg); 34165668Skris buffer_free(&msg); 34265668Skris} 34365668Skris 34492555Sdesstatic void 345126274Sdessend_attrib(u_int32_t id, const Attrib *a) 34665668Skris{ 34765668Skris Buffer msg; 34876259Sgreen 34999060Sdes TRACE("sent attrib id %u have 0x%x", id, a->flags); 35065668Skris buffer_init(&msg); 35176259Sgreen buffer_put_char(&msg, SSH2_FXP_ATTRS); 35265668Skris buffer_put_int(&msg, id); 35365668Skris encode_attrib(&msg, a); 35465668Skris send_msg(&msg); 35565668Skris buffer_free(&msg); 35665668Skris} 35765668Skris 35865668Skris/* parse incoming */ 35965668Skris 36092555Sdesstatic void 36165668Skrisprocess_init(void) 36265668Skris{ 36365668Skris Buffer msg; 36465668Skris 36598675Sdes version = get_int(); 36665668Skris TRACE("client version %d", version); 36765668Skris buffer_init(&msg); 36876259Sgreen buffer_put_char(&msg, SSH2_FXP_VERSION); 36976259Sgreen buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 37065668Skris send_msg(&msg); 37165668Skris buffer_free(&msg); 37265668Skris} 37365668Skris 37492555Sdesstatic void 37565668Skrisprocess_open(void) 37665668Skris{ 37765668Skris u_int32_t id, pflags; 37865668Skris Attrib *a; 37965668Skris char *name; 38076259Sgreen int handle, fd, flags, mode, status = SSH2_FX_FAILURE; 38165668Skris 38265668Skris id = get_int(); 38365668Skris name = get_string(NULL); 38476259Sgreen pflags = get_int(); /* portable flags */ 38565668Skris a = get_attrib(); 38665668Skris flags = flags_from_portable(pflags); 38776259Sgreen mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; 38899060Sdes TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode); 38965668Skris fd = open(name, flags, mode); 39065668Skris if (fd < 0) { 39165668Skris status = errno_to_portable(errno); 39265668Skris } else { 393113908Sdes handle = handle_new(HANDLE_FILE, name, fd, NULL); 39465668Skris if (handle < 0) { 39565668Skris close(fd); 39665668Skris } else { 39765668Skris send_handle(id, handle); 39876259Sgreen status = SSH2_FX_OK; 39965668Skris } 40065668Skris } 40176259Sgreen if (status != SSH2_FX_OK) 40265668Skris send_status(id, status); 40365668Skris xfree(name); 40465668Skris} 40565668Skris 40692555Sdesstatic void 40765668Skrisprocess_close(void) 40865668Skris{ 40965668Skris u_int32_t id; 41076259Sgreen int handle, ret, status = SSH2_FX_FAILURE; 41165668Skris 41265668Skris id = get_int(); 41365668Skris handle = get_handle(); 41499060Sdes TRACE("close id %u handle %d", id, handle); 41565668Skris ret = handle_close(handle); 41676259Sgreen status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 41765668Skris send_status(id, status); 41865668Skris} 41965668Skris 42092555Sdesstatic void 42165668Skrisprocess_read(void) 42265668Skris{ 42365668Skris char buf[64*1024]; 42476259Sgreen u_int32_t id, len; 42576259Sgreen int handle, fd, ret, status = SSH2_FX_FAILURE; 42665668Skris u_int64_t off; 42765668Skris 42865668Skris id = get_int(); 42965668Skris handle = get_handle(); 43076259Sgreen off = get_int64(); 43165668Skris len = get_int(); 43265668Skris 43399060Sdes TRACE("read id %u handle %d off %llu len %d", id, handle, 43498937Sdes (u_int64_t)off, len); 43565668Skris if (len > sizeof buf) { 43665668Skris len = sizeof buf; 437124208Sdes logit("read change len %d", len); 43865668Skris } 43965668Skris fd = handle_to_fd(handle); 44065668Skris if (fd >= 0) { 44165668Skris if (lseek(fd, off, SEEK_SET) < 0) { 44265668Skris error("process_read: seek failed"); 44365668Skris status = errno_to_portable(errno); 44465668Skris } else { 44565668Skris ret = read(fd, buf, len); 44665668Skris if (ret < 0) { 44765668Skris status = errno_to_portable(errno); 44865668Skris } else if (ret == 0) { 44976259Sgreen status = SSH2_FX_EOF; 45065668Skris } else { 45165668Skris send_data(id, buf, ret); 45276259Sgreen status = SSH2_FX_OK; 45365668Skris } 45465668Skris } 45565668Skris } 45676259Sgreen if (status != SSH2_FX_OK) 45765668Skris send_status(id, status); 45865668Skris} 45965668Skris 46092555Sdesstatic void 46165668Skrisprocess_write(void) 46265668Skris{ 46376259Sgreen u_int32_t id; 46465668Skris u_int64_t off; 46565668Skris u_int len; 46676259Sgreen int handle, fd, ret, status = SSH2_FX_FAILURE; 46765668Skris char *data; 46865668Skris 46965668Skris id = get_int(); 47065668Skris handle = get_handle(); 47176259Sgreen off = get_int64(); 47265668Skris data = get_string(&len); 47365668Skris 47499060Sdes TRACE("write id %u handle %d off %llu len %d", id, handle, 47598937Sdes (u_int64_t)off, len); 47665668Skris fd = handle_to_fd(handle); 47765668Skris if (fd >= 0) { 47865668Skris if (lseek(fd, off, SEEK_SET) < 0) { 47965668Skris status = errno_to_portable(errno); 48065668Skris error("process_write: seek failed"); 48165668Skris } else { 48265668Skris/* XXX ATOMICIO ? */ 48365668Skris ret = write(fd, data, len); 48465668Skris if (ret == -1) { 48565668Skris error("process_write: write failed"); 48665668Skris status = errno_to_portable(errno); 48765668Skris } else if (ret == len) { 48876259Sgreen status = SSH2_FX_OK; 48965668Skris } else { 490124208Sdes logit("nothing at all written"); 49165668Skris } 49265668Skris } 49365668Skris } 49465668Skris send_status(id, status); 49565668Skris xfree(data); 49665668Skris} 49765668Skris 49892555Sdesstatic void 49965668Skrisprocess_do_stat(int do_lstat) 50065668Skris{ 50176259Sgreen Attrib a; 50265668Skris struct stat st; 50365668Skris u_int32_t id; 50465668Skris char *name; 50576259Sgreen int ret, status = SSH2_FX_FAILURE; 50665668Skris 50765668Skris id = get_int(); 50865668Skris name = get_string(NULL); 50999060Sdes TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name); 51065668Skris ret = do_lstat ? lstat(name, &st) : stat(name, &st); 51165668Skris if (ret < 0) { 51265668Skris status = errno_to_portable(errno); 51365668Skris } else { 51476259Sgreen stat_to_attrib(&st, &a); 51576259Sgreen send_attrib(id, &a); 51676259Sgreen status = SSH2_FX_OK; 51765668Skris } 51876259Sgreen if (status != SSH2_FX_OK) 51965668Skris send_status(id, status); 52065668Skris xfree(name); 52165668Skris} 52265668Skris 52392555Sdesstatic void 52465668Skrisprocess_stat(void) 52565668Skris{ 52665668Skris process_do_stat(0); 52765668Skris} 52865668Skris 52992555Sdesstatic void 53065668Skrisprocess_lstat(void) 53165668Skris{ 53265668Skris process_do_stat(1); 53365668Skris} 53465668Skris 53592555Sdesstatic void 53665668Skrisprocess_fstat(void) 53765668Skris{ 53876259Sgreen Attrib a; 53965668Skris struct stat st; 54065668Skris u_int32_t id; 54176259Sgreen int fd, ret, handle, status = SSH2_FX_FAILURE; 54265668Skris 54365668Skris id = get_int(); 54465668Skris handle = get_handle(); 54599060Sdes TRACE("fstat id %u handle %d", id, handle); 54665668Skris fd = handle_to_fd(handle); 54765668Skris if (fd >= 0) { 54865668Skris ret = fstat(fd, &st); 54965668Skris if (ret < 0) { 55065668Skris status = errno_to_portable(errno); 55165668Skris } else { 55276259Sgreen stat_to_attrib(&st, &a); 55376259Sgreen send_attrib(id, &a); 55476259Sgreen status = SSH2_FX_OK; 55565668Skris } 55665668Skris } 55776259Sgreen if (status != SSH2_FX_OK) 55865668Skris send_status(id, status); 55965668Skris} 56065668Skris 56192555Sdesstatic struct timeval * 562126274Sdesattrib_to_tv(const Attrib *a) 56365668Skris{ 56465668Skris static struct timeval tv[2]; 56576259Sgreen 56665668Skris tv[0].tv_sec = a->atime; 56765668Skris tv[0].tv_usec = 0; 56865668Skris tv[1].tv_sec = a->mtime; 56965668Skris tv[1].tv_usec = 0; 57065668Skris return tv; 57165668Skris} 57265668Skris 57392555Sdesstatic void 57465668Skrisprocess_setstat(void) 57565668Skris{ 57665668Skris Attrib *a; 57765668Skris u_int32_t id; 57865668Skris char *name; 57999060Sdes int status = SSH2_FX_OK, ret; 58065668Skris 58165668Skris id = get_int(); 58265668Skris name = get_string(NULL); 58365668Skris a = get_attrib(); 58499060Sdes TRACE("setstat id %u name %s", id, name); 58592555Sdes if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 58692555Sdes ret = truncate(name, a->size); 58792555Sdes if (ret == -1) 58892555Sdes status = errno_to_portable(errno); 58992555Sdes } 59076259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 59165668Skris ret = chmod(name, a->perm & 0777); 59265668Skris if (ret == -1) 59365668Skris status = errno_to_portable(errno); 59465668Skris } 59576259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 59665668Skris ret = utimes(name, attrib_to_tv(a)); 59765668Skris if (ret == -1) 59865668Skris status = errno_to_portable(errno); 59965668Skris } 60076259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 60176259Sgreen ret = chown(name, a->uid, a->gid); 60276259Sgreen if (ret == -1) 60376259Sgreen status = errno_to_portable(errno); 60476259Sgreen } 60565668Skris send_status(id, status); 60665668Skris xfree(name); 60765668Skris} 60865668Skris 60992555Sdesstatic void 61065668Skrisprocess_fsetstat(void) 61165668Skris{ 61265668Skris Attrib *a; 61365668Skris u_int32_t id; 61465668Skris int handle, fd, ret; 61576259Sgreen int status = SSH2_FX_OK; 61698937Sdes char *name; 61765668Skris 61865668Skris id = get_int(); 61965668Skris handle = get_handle(); 62065668Skris a = get_attrib(); 62199060Sdes TRACE("fsetstat id %u handle %d", id, handle); 62265668Skris fd = handle_to_fd(handle); 62398937Sdes name = handle_to_name(handle); 62498937Sdes if (fd < 0 || name == NULL) { 62576259Sgreen status = SSH2_FX_FAILURE; 62665668Skris } else { 62792555Sdes if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 62892555Sdes ret = ftruncate(fd, a->size); 62992555Sdes if (ret == -1) 63092555Sdes status = errno_to_portable(errno); 63192555Sdes } 63276259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 63398937Sdes#ifdef HAVE_FCHMOD 63465668Skris ret = fchmod(fd, a->perm & 0777); 63598937Sdes#else 63698937Sdes ret = chmod(name, a->perm & 0777); 63798937Sdes#endif 63865668Skris if (ret == -1) 63965668Skris status = errno_to_portable(errno); 64065668Skris } 64176259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 64298937Sdes#ifdef HAVE_FUTIMES 64365668Skris ret = futimes(fd, attrib_to_tv(a)); 64498937Sdes#else 64598937Sdes ret = utimes(name, attrib_to_tv(a)); 64698937Sdes#endif 64765668Skris if (ret == -1) 64865668Skris status = errno_to_portable(errno); 64965668Skris } 65076259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 65198937Sdes#ifdef HAVE_FCHOWN 65276259Sgreen ret = fchown(fd, a->uid, a->gid); 65398937Sdes#else 65498937Sdes ret = chown(name, a->uid, a->gid); 65598937Sdes#endif 65676259Sgreen if (ret == -1) 65776259Sgreen status = errno_to_portable(errno); 65876259Sgreen } 65965668Skris } 66065668Skris send_status(id, status); 66165668Skris} 66265668Skris 66392555Sdesstatic void 66465668Skrisprocess_opendir(void) 66565668Skris{ 66665668Skris DIR *dirp = NULL; 66765668Skris char *path; 66876259Sgreen int handle, status = SSH2_FX_FAILURE; 66965668Skris u_int32_t id; 67065668Skris 67165668Skris id = get_int(); 67265668Skris path = get_string(NULL); 67399060Sdes TRACE("opendir id %u path %s", id, path); 67476259Sgreen dirp = opendir(path); 67565668Skris if (dirp == NULL) { 67665668Skris status = errno_to_portable(errno); 67765668Skris } else { 678113908Sdes handle = handle_new(HANDLE_DIR, path, 0, dirp); 67965668Skris if (handle < 0) { 68065668Skris closedir(dirp); 68165668Skris } else { 68265668Skris send_handle(id, handle); 68376259Sgreen status = SSH2_FX_OK; 68465668Skris } 68576259Sgreen 68665668Skris } 68776259Sgreen if (status != SSH2_FX_OK) 68865668Skris send_status(id, status); 68965668Skris xfree(path); 69065668Skris} 69165668Skris 69292555Sdesstatic void 69365668Skrisprocess_readdir(void) 69465668Skris{ 69565668Skris DIR *dirp; 69665668Skris struct dirent *dp; 69765668Skris char *path; 69865668Skris int handle; 69965668Skris u_int32_t id; 70065668Skris 70165668Skris id = get_int(); 70265668Skris handle = get_handle(); 70399060Sdes TRACE("readdir id %u handle %d", id, handle); 70465668Skris dirp = handle_to_dir(handle); 70565668Skris path = handle_to_name(handle); 70665668Skris if (dirp == NULL || path == NULL) { 70776259Sgreen send_status(id, SSH2_FX_FAILURE); 70865668Skris } else { 70965668Skris struct stat st; 71065668Skris char pathname[1024]; 71165668Skris Stat *stats; 71265668Skris int nstats = 10, count = 0, i; 71399060Sdes 71465668Skris stats = xmalloc(nstats * sizeof(Stat)); 71565668Skris while ((dp = readdir(dirp)) != NULL) { 71665668Skris if (count >= nstats) { 71765668Skris nstats *= 2; 71865668Skris stats = xrealloc(stats, nstats * sizeof(Stat)); 71965668Skris } 72065668Skris/* XXX OVERFLOW ? */ 72192555Sdes snprintf(pathname, sizeof pathname, "%s%s%s", path, 72292555Sdes strcmp(path, "/") ? "/" : "", dp->d_name); 72365668Skris if (lstat(pathname, &st) < 0) 72465668Skris continue; 72576259Sgreen stat_to_attrib(&st, &(stats[count].attrib)); 72665668Skris stats[count].name = xstrdup(dp->d_name); 727106121Sdes stats[count].long_name = ls_file(dp->d_name, &st, 0); 72865668Skris count++; 72965668Skris /* send up to 100 entries in one message */ 73076259Sgreen /* XXX check packet size instead */ 73165668Skris if (count == 100) 73265668Skris break; 73365668Skris } 73476259Sgreen if (count > 0) { 73576259Sgreen send_names(id, count, stats); 73692555Sdes for (i = 0; i < count; i++) { 73776259Sgreen xfree(stats[i].name); 73876259Sgreen xfree(stats[i].long_name); 73976259Sgreen } 74076259Sgreen } else { 74176259Sgreen send_status(id, SSH2_FX_EOF); 74265668Skris } 74365668Skris xfree(stats); 74465668Skris } 74565668Skris} 74665668Skris 74792555Sdesstatic void 74865668Skrisprocess_remove(void) 74965668Skris{ 75065668Skris char *name; 75165668Skris u_int32_t id; 75276259Sgreen int status = SSH2_FX_FAILURE; 75365668Skris int ret; 75465668Skris 75565668Skris id = get_int(); 75665668Skris name = get_string(NULL); 75799060Sdes TRACE("remove id %u name %s", id, name); 75876259Sgreen ret = unlink(name); 75976259Sgreen status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 76065668Skris send_status(id, status); 76165668Skris xfree(name); 76265668Skris} 76365668Skris 76492555Sdesstatic void 76565668Skrisprocess_mkdir(void) 76665668Skris{ 76765668Skris Attrib *a; 76865668Skris u_int32_t id; 76965668Skris char *name; 77076259Sgreen int ret, mode, status = SSH2_FX_FAILURE; 77165668Skris 77265668Skris id = get_int(); 77365668Skris name = get_string(NULL); 77465668Skris a = get_attrib(); 77576259Sgreen mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 77676259Sgreen a->perm & 0777 : 0777; 77799060Sdes TRACE("mkdir id %u name %s mode 0%o", id, name, mode); 77865668Skris ret = mkdir(name, mode); 77976259Sgreen status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 78065668Skris send_status(id, status); 78165668Skris xfree(name); 78265668Skris} 78365668Skris 78492555Sdesstatic void 78565668Skrisprocess_rmdir(void) 78665668Skris{ 78765668Skris u_int32_t id; 78865668Skris char *name; 78965668Skris int ret, status; 79065668Skris 79165668Skris id = get_int(); 79265668Skris name = get_string(NULL); 79399060Sdes TRACE("rmdir id %u name %s", id, name); 79465668Skris ret = rmdir(name); 79576259Sgreen status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 79665668Skris send_status(id, status); 79765668Skris xfree(name); 79865668Skris} 79965668Skris 80092555Sdesstatic void 80165668Skrisprocess_realpath(void) 80265668Skris{ 80365668Skris char resolvedname[MAXPATHLEN]; 80465668Skris u_int32_t id; 80565668Skris char *path; 80665668Skris 80765668Skris id = get_int(); 80865668Skris path = get_string(NULL); 80976259Sgreen if (path[0] == '\0') { 81076259Sgreen xfree(path); 81176259Sgreen path = xstrdup("."); 81276259Sgreen } 81399060Sdes TRACE("realpath id %u path %s", id, path); 81465668Skris if (realpath(path, resolvedname) == NULL) { 81565668Skris send_status(id, errno_to_portable(errno)); 81665668Skris } else { 81765668Skris Stat s; 81865668Skris attrib_clear(&s.attrib); 81965668Skris s.name = s.long_name = resolvedname; 82065668Skris send_names(id, 1, &s); 82165668Skris } 82265668Skris xfree(path); 82365668Skris} 82465668Skris 82592555Sdesstatic void 82665668Skrisprocess_rename(void) 82765668Skris{ 82865668Skris u_int32_t id; 82965668Skris char *oldpath, *newpath; 830113908Sdes int status; 831113908Sdes struct stat sb; 83265668Skris 83365668Skris id = get_int(); 83465668Skris oldpath = get_string(NULL); 83565668Skris newpath = get_string(NULL); 83699060Sdes TRACE("rename id %u old %s new %s", id, oldpath, newpath); 837113908Sdes status = SSH2_FX_FAILURE; 838113908Sdes if (lstat(oldpath, &sb) == -1) 839113908Sdes status = errno_to_portable(errno); 840113908Sdes else if (S_ISREG(sb.st_mode)) { 841113908Sdes /* Race-free rename of regular files */ 842113908Sdes if (link(oldpath, newpath) == -1) 843113908Sdes status = errno_to_portable(errno); 844113908Sdes else if (unlink(oldpath) == -1) { 845113908Sdes status = errno_to_portable(errno); 846113908Sdes /* clean spare link */ 847113908Sdes unlink(newpath); 848113908Sdes } else 849113908Sdes status = SSH2_FX_OK; 850113908Sdes } else if (stat(newpath, &sb) == -1) { 851113908Sdes if (rename(oldpath, newpath) == -1) 852113908Sdes status = errno_to_portable(errno); 853113908Sdes else 854113908Sdes status = SSH2_FX_OK; 85576259Sgreen } 85665668Skris send_status(id, status); 85765668Skris xfree(oldpath); 85865668Skris xfree(newpath); 85965668Skris} 86065668Skris 86192555Sdesstatic void 86276259Sgreenprocess_readlink(void) 86376259Sgreen{ 86476259Sgreen u_int32_t id; 86592555Sdes int len; 86676259Sgreen char link[MAXPATHLEN]; 86776259Sgreen char *path; 86865668Skris 86976259Sgreen id = get_int(); 87076259Sgreen path = get_string(NULL); 87199060Sdes TRACE("readlink id %u path %s", id, path); 87292555Sdes if ((len = readlink(path, link, sizeof(link) - 1)) == -1) 87376259Sgreen send_status(id, errno_to_portable(errno)); 87476259Sgreen else { 87576259Sgreen Stat s; 87692555Sdes 87792555Sdes link[len] = '\0'; 87876259Sgreen attrib_clear(&s.attrib); 87976259Sgreen s.name = s.long_name = link; 88076259Sgreen send_names(id, 1, &s); 88176259Sgreen } 88276259Sgreen xfree(path); 88376259Sgreen} 88476259Sgreen 88592555Sdesstatic void 88676259Sgreenprocess_symlink(void) 88776259Sgreen{ 88876259Sgreen u_int32_t id; 88976259Sgreen char *oldpath, *newpath; 890113908Sdes int ret, status; 89176259Sgreen 89276259Sgreen id = get_int(); 89376259Sgreen oldpath = get_string(NULL); 89476259Sgreen newpath = get_string(NULL); 89599060Sdes TRACE("symlink id %u old %s new %s", id, oldpath, newpath); 896113908Sdes /* this will fail if 'newpath' exists */ 897113908Sdes ret = symlink(oldpath, newpath); 898113908Sdes status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 89976259Sgreen send_status(id, status); 90076259Sgreen xfree(oldpath); 90176259Sgreen xfree(newpath); 90276259Sgreen} 90376259Sgreen 90492555Sdesstatic void 90576259Sgreenprocess_extended(void) 90676259Sgreen{ 90776259Sgreen u_int32_t id; 90876259Sgreen char *request; 90976259Sgreen 91076259Sgreen id = get_int(); 91176259Sgreen request = get_string(NULL); 91276259Sgreen send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 91376259Sgreen xfree(request); 91476259Sgreen} 91576259Sgreen 91665668Skris/* stolen from ssh-agent */ 91765668Skris 91892555Sdesstatic void 91965668Skrisprocess(void) 92065668Skris{ 92176259Sgreen u_int msg_len; 92298675Sdes u_int buf_len; 92398675Sdes u_int consumed; 92476259Sgreen u_int type; 92576259Sgreen u_char *cp; 92665668Skris 92798675Sdes buf_len = buffer_len(&iqueue); 92898675Sdes if (buf_len < 5) 92965668Skris return; /* Incomplete message. */ 93092555Sdes cp = buffer_ptr(&iqueue); 93165668Skris msg_len = GET_32BIT(cp); 93265668Skris if (msg_len > 256 * 1024) { 93365668Skris error("bad message "); 93465668Skris exit(11); 93565668Skris } 93698675Sdes if (buf_len < msg_len + 4) 93765668Skris return; 93865668Skris buffer_consume(&iqueue, 4); 93998675Sdes buf_len -= 4; 94065668Skris type = buffer_get_char(&iqueue); 94165668Skris switch (type) { 94276259Sgreen case SSH2_FXP_INIT: 94365668Skris process_init(); 94465668Skris break; 94576259Sgreen case SSH2_FXP_OPEN: 94665668Skris process_open(); 94765668Skris break; 94876259Sgreen case SSH2_FXP_CLOSE: 94965668Skris process_close(); 95065668Skris break; 95176259Sgreen case SSH2_FXP_READ: 95265668Skris process_read(); 95365668Skris break; 95476259Sgreen case SSH2_FXP_WRITE: 95565668Skris process_write(); 95665668Skris break; 95776259Sgreen case SSH2_FXP_LSTAT: 95865668Skris process_lstat(); 95965668Skris break; 96076259Sgreen case SSH2_FXP_FSTAT: 96165668Skris process_fstat(); 96265668Skris break; 96376259Sgreen case SSH2_FXP_SETSTAT: 96465668Skris process_setstat(); 96565668Skris break; 96676259Sgreen case SSH2_FXP_FSETSTAT: 96765668Skris process_fsetstat(); 96865668Skris break; 96976259Sgreen case SSH2_FXP_OPENDIR: 97065668Skris process_opendir(); 97165668Skris break; 97276259Sgreen case SSH2_FXP_READDIR: 97365668Skris process_readdir(); 97465668Skris break; 97576259Sgreen case SSH2_FXP_REMOVE: 97665668Skris process_remove(); 97765668Skris break; 97876259Sgreen case SSH2_FXP_MKDIR: 97965668Skris process_mkdir(); 98065668Skris break; 98176259Sgreen case SSH2_FXP_RMDIR: 98265668Skris process_rmdir(); 98365668Skris break; 98476259Sgreen case SSH2_FXP_REALPATH: 98565668Skris process_realpath(); 98665668Skris break; 98776259Sgreen case SSH2_FXP_STAT: 98865668Skris process_stat(); 98965668Skris break; 99076259Sgreen case SSH2_FXP_RENAME: 99165668Skris process_rename(); 99265668Skris break; 99376259Sgreen case SSH2_FXP_READLINK: 99476259Sgreen process_readlink(); 99576259Sgreen break; 99676259Sgreen case SSH2_FXP_SYMLINK: 99776259Sgreen process_symlink(); 99876259Sgreen break; 99976259Sgreen case SSH2_FXP_EXTENDED: 100076259Sgreen process_extended(); 100176259Sgreen break; 100265668Skris default: 100365668Skris error("Unknown message %d", type); 100465668Skris break; 100565668Skris } 100698675Sdes /* discard the remaining bytes from the current packet */ 100798675Sdes if (buf_len < buffer_len(&iqueue)) 100898675Sdes fatal("iqueue grows"); 100998675Sdes consumed = buf_len - buffer_len(&iqueue); 101098675Sdes if (msg_len < consumed) 101198675Sdes fatal("msg_len %d < consumed %d", msg_len, consumed); 101298675Sdes if (msg_len > consumed) 101398675Sdes buffer_consume(&iqueue, msg_len - consumed); 101465668Skris} 101565668Skris 101665668Skrisint 101765668Skrismain(int ac, char **av) 101865668Skris{ 101976259Sgreen fd_set *rset, *wset; 102065668Skris int in, out, max; 102176259Sgreen ssize_t len, olen, set_size; 102265668Skris 102376259Sgreen /* XXX should use getopt */ 102476259Sgreen 1025124208Sdes __progname = ssh_get_progname(av[0]); 102665668Skris handle_init(); 102765668Skris 102876259Sgreen#ifdef DEBUG_SFTP_SERVER 102976259Sgreen log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); 103076259Sgreen#endif 103176259Sgreen 103265668Skris in = dup(STDIN_FILENO); 103365668Skris out = dup(STDOUT_FILENO); 103465668Skris 103598937Sdes#ifdef HAVE_CYGWIN 103698937Sdes setmode(in, O_BINARY); 103798937Sdes setmode(out, O_BINARY); 103898937Sdes#endif 103998937Sdes 104065668Skris max = 0; 104165668Skris if (in > max) 104265668Skris max = in; 104365668Skris if (out > max) 104465668Skris max = out; 104565668Skris 104665668Skris buffer_init(&iqueue); 104765668Skris buffer_init(&oqueue); 104865668Skris 104976259Sgreen set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 105076259Sgreen rset = (fd_set *)xmalloc(set_size); 105176259Sgreen wset = (fd_set *)xmalloc(set_size); 105276259Sgreen 105365668Skris for (;;) { 105476259Sgreen memset(rset, 0, set_size); 105576259Sgreen memset(wset, 0, set_size); 105665668Skris 105776259Sgreen FD_SET(in, rset); 105865668Skris olen = buffer_len(&oqueue); 105965668Skris if (olen > 0) 106076259Sgreen FD_SET(out, wset); 106165668Skris 106276259Sgreen if (select(max+1, rset, wset, NULL, NULL) < 0) { 106365668Skris if (errno == EINTR) 106465668Skris continue; 106565668Skris exit(2); 106665668Skris } 106765668Skris 106865668Skris /* copy stdin to iqueue */ 106976259Sgreen if (FD_ISSET(in, rset)) { 107065668Skris char buf[4*4096]; 107165668Skris len = read(in, buf, sizeof buf); 107265668Skris if (len == 0) { 107365668Skris debug("read eof"); 107465668Skris exit(0); 107565668Skris } else if (len < 0) { 107665668Skris error("read error"); 107765668Skris exit(1); 107865668Skris } else { 107965668Skris buffer_append(&iqueue, buf, len); 108065668Skris } 108165668Skris } 108265668Skris /* send oqueue to stdout */ 108376259Sgreen if (FD_ISSET(out, wset)) { 108465668Skris len = write(out, buffer_ptr(&oqueue), olen); 108565668Skris if (len < 0) { 108665668Skris error("write error"); 108765668Skris exit(1); 108865668Skris } else { 108965668Skris buffer_consume(&oqueue, len); 109065668Skris } 109165668Skris } 109265668Skris /* process requests from client */ 109365668Skris process(); 109465668Skris } 109565668Skris} 1096