sftp-server.c revision 92555
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" 2592555SdesRCSID("$OpenBSD: sftp-server.c,v 1.33 2002/02/13 00:28:13 markus 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 4265668Skris/* input and output queue */ 4365668SkrisBuffer iqueue; 4465668SkrisBuffer oqueue; 4565668Skris 4676259Sgreen/* Version of client */ 4776259Sgreenint version; 4876259Sgreen 4965668Skris/* portable attibutes, etc. */ 5065668Skris 5165668Skristypedef struct Stat Stat; 5265668Skris 5376259Sgreenstruct Stat { 5465668Skris char *name; 5565668Skris char *long_name; 5665668Skris Attrib attrib; 5765668Skris}; 5865668Skris 5992555Sdesstatic int 6065668Skriserrno_to_portable(int unixerrno) 6165668Skris{ 6265668Skris int ret = 0; 6376259Sgreen 6465668Skris switch (unixerrno) { 6565668Skris case 0: 6676259Sgreen ret = SSH2_FX_OK; 6765668Skris break; 6865668Skris case ENOENT: 6965668Skris case ENOTDIR: 7065668Skris case EBADF: 7165668Skris case ELOOP: 7276259Sgreen ret = SSH2_FX_NO_SUCH_FILE; 7365668Skris break; 7465668Skris case EPERM: 7565668Skris case EACCES: 7665668Skris case EFAULT: 7776259Sgreen ret = SSH2_FX_PERMISSION_DENIED; 7865668Skris break; 7965668Skris case ENAMETOOLONG: 8065668Skris case EINVAL: 8176259Sgreen ret = SSH2_FX_BAD_MESSAGE; 8265668Skris break; 8365668Skris default: 8476259Sgreen ret = SSH2_FX_FAILURE; 8565668Skris break; 8665668Skris } 8765668Skris return ret; 8865668Skris} 8965668Skris 9092555Sdesstatic int 9165668Skrisflags_from_portable(int pflags) 9265668Skris{ 9365668Skris int flags = 0; 9476259Sgreen 9576259Sgreen if ((pflags & SSH2_FXF_READ) && 9676259Sgreen (pflags & SSH2_FXF_WRITE)) { 9765668Skris flags = O_RDWR; 9876259Sgreen } else if (pflags & SSH2_FXF_READ) { 9965668Skris flags = O_RDONLY; 10076259Sgreen } else if (pflags & SSH2_FXF_WRITE) { 10165668Skris flags = O_WRONLY; 10265668Skris } 10376259Sgreen if (pflags & SSH2_FXF_CREAT) 10465668Skris flags |= O_CREAT; 10576259Sgreen if (pflags & SSH2_FXF_TRUNC) 10665668Skris flags |= O_TRUNC; 10776259Sgreen if (pflags & SSH2_FXF_EXCL) 10865668Skris flags |= O_EXCL; 10965668Skris return flags; 11065668Skris} 11165668Skris 11292555Sdesstatic Attrib * 11365668Skrisget_attrib(void) 11465668Skris{ 11565668Skris return decode_attrib(&iqueue); 11665668Skris} 11765668Skris 11865668Skris/* handle handles */ 11965668Skris 12065668Skristypedef struct Handle Handle; 12165668Skrisstruct Handle { 12265668Skris int use; 12365668Skris DIR *dirp; 12465668Skris int fd; 12565668Skris char *name; 12665668Skris}; 12776259Sgreen 12865668Skrisenum { 12965668Skris HANDLE_UNUSED, 13065668Skris HANDLE_DIR, 13165668Skris HANDLE_FILE 13265668Skris}; 13376259Sgreen 13465668SkrisHandle handles[100]; 13565668Skris 13692555Sdesstatic void 13765668Skrishandle_init(void) 13865668Skris{ 13965668Skris int i; 14076259Sgreen 14192555Sdes for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) 14265668Skris handles[i].use = HANDLE_UNUSED; 14365668Skris} 14465668Skris 14592555Sdesstatic int 14665668Skrishandle_new(int use, char *name, int fd, DIR *dirp) 14765668Skris{ 14865668Skris int i; 14976259Sgreen 15092555Sdes for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) { 15165668Skris if (handles[i].use == HANDLE_UNUSED) { 15265668Skris handles[i].use = use; 15365668Skris handles[i].dirp = dirp; 15465668Skris handles[i].fd = fd; 15565668Skris handles[i].name = name; 15665668Skris return i; 15765668Skris } 15865668Skris } 15965668Skris return -1; 16065668Skris} 16165668Skris 16292555Sdesstatic int 16365668Skrishandle_is_ok(int i, int type) 16465668Skris{ 16576259Sgreen return i >= 0 && i < sizeof(handles)/sizeof(Handle) && 16676259Sgreen handles[i].use == type; 16765668Skris} 16865668Skris 16992555Sdesstatic int 17065668Skrishandle_to_string(int handle, char **stringp, int *hlenp) 17165668Skris{ 17265668Skris if (stringp == NULL || hlenp == NULL) 17365668Skris return -1; 17476259Sgreen *stringp = xmalloc(sizeof(int32_t)); 17576259Sgreen PUT_32BIT(*stringp, handle); 17676259Sgreen *hlenp = sizeof(int32_t); 17765668Skris return 0; 17865668Skris} 17965668Skris 18092555Sdesstatic int 18165668Skrishandle_from_string(char *handle, u_int hlen) 18265668Skris{ 18376259Sgreen int val; 18476259Sgreen 18576259Sgreen if (hlen != sizeof(int32_t)) 18665668Skris return -1; 18776259Sgreen val = GET_32BIT(handle); 18865668Skris if (handle_is_ok(val, HANDLE_FILE) || 18965668Skris handle_is_ok(val, HANDLE_DIR)) 19065668Skris return val; 19165668Skris return -1; 19265668Skris} 19365668Skris 19492555Sdesstatic char * 19565668Skrishandle_to_name(int handle) 19665668Skris{ 19765668Skris if (handle_is_ok(handle, HANDLE_DIR)|| 19865668Skris handle_is_ok(handle, HANDLE_FILE)) 19965668Skris return handles[handle].name; 20065668Skris return NULL; 20165668Skris} 20265668Skris 20392555Sdesstatic DIR * 20465668Skrishandle_to_dir(int handle) 20565668Skris{ 20665668Skris if (handle_is_ok(handle, HANDLE_DIR)) 20765668Skris return handles[handle].dirp; 20865668Skris return NULL; 20965668Skris} 21065668Skris 21192555Sdesstatic int 21265668Skrishandle_to_fd(int handle) 21365668Skris{ 21476259Sgreen if (handle_is_ok(handle, HANDLE_FILE)) 21565668Skris return handles[handle].fd; 21665668Skris return -1; 21765668Skris} 21865668Skris 21992555Sdesstatic int 22065668Skrishandle_close(int handle) 22165668Skris{ 22265668Skris int ret = -1; 22376259Sgreen 22465668Skris if (handle_is_ok(handle, HANDLE_FILE)) { 22565668Skris ret = close(handles[handle].fd); 22665668Skris handles[handle].use = HANDLE_UNUSED; 22765668Skris } else if (handle_is_ok(handle, HANDLE_DIR)) { 22865668Skris ret = closedir(handles[handle].dirp); 22965668Skris handles[handle].use = HANDLE_UNUSED; 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 27965668Skris TRACE("sent status id %d error %d", 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 29365668Skrissend_data_or_handle(char type, u_int32_t id, 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 30665668Skrissend_data(u_int32_t id, char *data, int dlen) 30765668Skris{ 30865668Skris TRACE("sent data id %d 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); 31965668Skris TRACE("sent handle id %d handle %d", id, handle); 32076259Sgreen send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 32165668Skris xfree(string); 32265668Skris} 32365668Skris 32492555Sdesstatic void 32565668Skrissend_names(u_int32_t id, int count, 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); 33465668Skris TRACE("sent names id %d 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 34565668Skrissend_attrib(u_int32_t id, Attrib *a) 34665668Skris{ 34765668Skris Buffer msg; 34876259Sgreen 34965668Skris TRACE("sent attrib id %d 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 36576259Sgreen version = buffer_get_int(&iqueue); 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; 38865668Skris TRACE("open id %d 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 { 39365668Skris handle = handle_new(HANDLE_FILE, xstrdup(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(); 41465668Skris TRACE("close id %d 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 43376259Sgreen TRACE("read id %d handle %d off %llu len %d", id, handle, 43476259Sgreen (unsigned long long)off, len); 43565668Skris if (len > sizeof buf) { 43665668Skris len = sizeof buf; 43765668Skris log("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 47476259Sgreen TRACE("write id %d handle %d off %llu len %d", id, handle, 47576259Sgreen (unsigned long long)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 { 49065668Skris log("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); 50965668Skris TRACE("%sstat id %d 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(); 54565668Skris TRACE("fstat id %d 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 * 56265668Skrisattrib_to_tv(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; 57965668Skris int ret; 58076259Sgreen int status = SSH2_FX_OK; 58165668Skris 58265668Skris id = get_int(); 58365668Skris name = get_string(NULL); 58465668Skris a = get_attrib(); 58565668Skris TRACE("setstat id %d name %s", id, name); 58692555Sdes if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 58792555Sdes ret = truncate(name, a->size); 58892555Sdes if (ret == -1) 58992555Sdes status = errno_to_portable(errno); 59092555Sdes } 59176259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 59265668Skris ret = chmod(name, a->perm & 0777); 59365668Skris if (ret == -1) 59465668Skris status = errno_to_portable(errno); 59565668Skris } 59676259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 59765668Skris ret = utimes(name, attrib_to_tv(a)); 59865668Skris if (ret == -1) 59965668Skris status = errno_to_portable(errno); 60065668Skris } 60176259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 60276259Sgreen ret = chown(name, a->uid, a->gid); 60376259Sgreen if (ret == -1) 60476259Sgreen status = errno_to_portable(errno); 60576259Sgreen } 60665668Skris send_status(id, status); 60765668Skris xfree(name); 60865668Skris} 60965668Skris 61092555Sdesstatic void 61165668Skrisprocess_fsetstat(void) 61265668Skris{ 61365668Skris Attrib *a; 61465668Skris u_int32_t id; 61565668Skris int handle, fd, ret; 61676259Sgreen int status = SSH2_FX_OK; 61765668Skris 61865668Skris id = get_int(); 61965668Skris handle = get_handle(); 62065668Skris a = get_attrib(); 62165668Skris TRACE("fsetstat id %d handle %d", id, handle); 62265668Skris fd = handle_to_fd(handle); 62365668Skris if (fd < 0) { 62476259Sgreen status = SSH2_FX_FAILURE; 62565668Skris } else { 62692555Sdes if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 62792555Sdes ret = ftruncate(fd, a->size); 62892555Sdes if (ret == -1) 62992555Sdes status = errno_to_portable(errno); 63092555Sdes } 63176259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 63265668Skris ret = fchmod(fd, a->perm & 0777); 63365668Skris if (ret == -1) 63465668Skris status = errno_to_portable(errno); 63565668Skris } 63676259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 63765668Skris ret = futimes(fd, attrib_to_tv(a)); 63865668Skris if (ret == -1) 63965668Skris status = errno_to_portable(errno); 64065668Skris } 64176259Sgreen if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 64276259Sgreen ret = fchown(fd, a->uid, a->gid); 64376259Sgreen if (ret == -1) 64476259Sgreen status = errno_to_portable(errno); 64576259Sgreen } 64665668Skris } 64765668Skris send_status(id, status); 64865668Skris} 64965668Skris 65092555Sdesstatic void 65165668Skrisprocess_opendir(void) 65265668Skris{ 65365668Skris DIR *dirp = NULL; 65465668Skris char *path; 65576259Sgreen int handle, status = SSH2_FX_FAILURE; 65665668Skris u_int32_t id; 65765668Skris 65865668Skris id = get_int(); 65965668Skris path = get_string(NULL); 66065668Skris TRACE("opendir id %d path %s", id, path); 66176259Sgreen dirp = opendir(path); 66265668Skris if (dirp == NULL) { 66365668Skris status = errno_to_portable(errno); 66465668Skris } else { 66565668Skris handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp); 66665668Skris if (handle < 0) { 66765668Skris closedir(dirp); 66865668Skris } else { 66965668Skris send_handle(id, handle); 67076259Sgreen status = SSH2_FX_OK; 67165668Skris } 67276259Sgreen 67365668Skris } 67476259Sgreen if (status != SSH2_FX_OK) 67565668Skris send_status(id, status); 67665668Skris xfree(path); 67765668Skris} 67865668Skris 67976259Sgreen/* 68076259Sgreen * drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh 68176259Sgreen */ 68292555Sdesstatic char * 68365668Skrisls_file(char *name, struct stat *st) 68465668Skris{ 68592555Sdes int ulen, glen, sz = 0; 68676259Sgreen struct passwd *pw; 68776259Sgreen struct group *gr; 68876259Sgreen struct tm *ltime = localtime(&st->st_mtime); 68976259Sgreen char *user, *group; 69076259Sgreen char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1]; 69176259Sgreen 69276259Sgreen strmode(st->st_mode, mode); 69376259Sgreen if ((pw = getpwuid(st->st_uid)) != NULL) { 69476259Sgreen user = pw->pw_name; 69576259Sgreen } else { 69676259Sgreen snprintf(ubuf, sizeof ubuf, "%d", st->st_uid); 69776259Sgreen user = ubuf; 69876259Sgreen } 69976259Sgreen if ((gr = getgrgid(st->st_gid)) != NULL) { 70076259Sgreen group = gr->gr_name; 70176259Sgreen } else { 70276259Sgreen snprintf(gbuf, sizeof gbuf, "%d", st->st_gid); 70376259Sgreen group = gbuf; 70476259Sgreen } 70576259Sgreen if (ltime != NULL) { 70676259Sgreen if (time(NULL) - st->st_mtime < (365*24*60*60)/2) 70776259Sgreen sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime); 70876259Sgreen else 70976259Sgreen sz = strftime(tbuf, sizeof tbuf, "%b %e %Y", ltime); 71076259Sgreen } 71176259Sgreen if (sz == 0) 71276259Sgreen tbuf[0] = '\0'; 71392555Sdes ulen = MAX(strlen(user), 8); 71492555Sdes glen = MAX(strlen(group), 8); 71592555Sdes snprintf(buf, sizeof buf, "%s %3d %-*s %-*s %8llu %s %s", mode, 71692555Sdes st->st_nlink, ulen, user, glen, group, 71792555Sdes (unsigned long long)st->st_size, tbuf, name); 71865668Skris return xstrdup(buf); 71965668Skris} 72065668Skris 72192555Sdesstatic void 72265668Skrisprocess_readdir(void) 72365668Skris{ 72465668Skris DIR *dirp; 72565668Skris struct dirent *dp; 72665668Skris char *path; 72765668Skris int handle; 72865668Skris u_int32_t id; 72965668Skris 73065668Skris id = get_int(); 73165668Skris handle = get_handle(); 73265668Skris TRACE("readdir id %d handle %d", id, handle); 73365668Skris dirp = handle_to_dir(handle); 73465668Skris path = handle_to_name(handle); 73565668Skris if (dirp == NULL || path == NULL) { 73676259Sgreen send_status(id, SSH2_FX_FAILURE); 73765668Skris } else { 73865668Skris struct stat st; 73965668Skris char pathname[1024]; 74065668Skris Stat *stats; 74165668Skris int nstats = 10, count = 0, i; 74265668Skris stats = xmalloc(nstats * sizeof(Stat)); 74365668Skris while ((dp = readdir(dirp)) != NULL) { 74465668Skris if (count >= nstats) { 74565668Skris nstats *= 2; 74665668Skris stats = xrealloc(stats, nstats * sizeof(Stat)); 74765668Skris } 74865668Skris/* XXX OVERFLOW ? */ 74992555Sdes snprintf(pathname, sizeof pathname, "%s%s%s", path, 75092555Sdes strcmp(path, "/") ? "/" : "", dp->d_name); 75165668Skris if (lstat(pathname, &st) < 0) 75265668Skris continue; 75376259Sgreen stat_to_attrib(&st, &(stats[count].attrib)); 75465668Skris stats[count].name = xstrdup(dp->d_name); 75565668Skris stats[count].long_name = ls_file(dp->d_name, &st); 75665668Skris count++; 75765668Skris /* send up to 100 entries in one message */ 75876259Sgreen /* XXX check packet size instead */ 75965668Skris if (count == 100) 76065668Skris break; 76165668Skris } 76276259Sgreen if (count > 0) { 76376259Sgreen send_names(id, count, stats); 76492555Sdes for (i = 0; i < count; i++) { 76576259Sgreen xfree(stats[i].name); 76676259Sgreen xfree(stats[i].long_name); 76776259Sgreen } 76876259Sgreen } else { 76976259Sgreen send_status(id, SSH2_FX_EOF); 77065668Skris } 77165668Skris xfree(stats); 77265668Skris } 77365668Skris} 77465668Skris 77592555Sdesstatic void 77665668Skrisprocess_remove(void) 77765668Skris{ 77865668Skris char *name; 77965668Skris u_int32_t id; 78076259Sgreen int status = SSH2_FX_FAILURE; 78165668Skris int ret; 78265668Skris 78365668Skris id = get_int(); 78465668Skris name = get_string(NULL); 78565668Skris TRACE("remove id %d name %s", id, name); 78676259Sgreen ret = unlink(name); 78776259Sgreen status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 78865668Skris send_status(id, status); 78965668Skris xfree(name); 79065668Skris} 79165668Skris 79292555Sdesstatic void 79365668Skrisprocess_mkdir(void) 79465668Skris{ 79565668Skris Attrib *a; 79665668Skris u_int32_t id; 79765668Skris char *name; 79876259Sgreen int ret, mode, status = SSH2_FX_FAILURE; 79965668Skris 80065668Skris id = get_int(); 80165668Skris name = get_string(NULL); 80265668Skris a = get_attrib(); 80376259Sgreen mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 80476259Sgreen a->perm & 0777 : 0777; 80565668Skris TRACE("mkdir id %d name %s mode 0%o", id, name, mode); 80665668Skris ret = mkdir(name, mode); 80776259Sgreen status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 80865668Skris send_status(id, status); 80965668Skris xfree(name); 81065668Skris} 81165668Skris 81292555Sdesstatic void 81365668Skrisprocess_rmdir(void) 81465668Skris{ 81565668Skris u_int32_t id; 81665668Skris char *name; 81765668Skris int ret, status; 81865668Skris 81965668Skris id = get_int(); 82065668Skris name = get_string(NULL); 82165668Skris TRACE("rmdir id %d name %s", id, name); 82265668Skris ret = rmdir(name); 82376259Sgreen status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 82465668Skris send_status(id, status); 82565668Skris xfree(name); 82665668Skris} 82765668Skris 82892555Sdesstatic void 82965668Skrisprocess_realpath(void) 83065668Skris{ 83165668Skris char resolvedname[MAXPATHLEN]; 83265668Skris u_int32_t id; 83365668Skris char *path; 83465668Skris 83565668Skris id = get_int(); 83665668Skris path = get_string(NULL); 83776259Sgreen if (path[0] == '\0') { 83876259Sgreen xfree(path); 83976259Sgreen path = xstrdup("."); 84076259Sgreen } 84165668Skris TRACE("realpath id %d path %s", id, path); 84265668Skris if (realpath(path, resolvedname) == NULL) { 84365668Skris send_status(id, errno_to_portable(errno)); 84465668Skris } else { 84565668Skris Stat s; 84665668Skris attrib_clear(&s.attrib); 84765668Skris s.name = s.long_name = resolvedname; 84865668Skris send_names(id, 1, &s); 84965668Skris } 85065668Skris xfree(path); 85165668Skris} 85265668Skris 85392555Sdesstatic void 85465668Skrisprocess_rename(void) 85565668Skris{ 85665668Skris u_int32_t id; 85776259Sgreen struct stat st; 85865668Skris char *oldpath, *newpath; 85976259Sgreen int ret, status = SSH2_FX_FAILURE; 86065668Skris 86165668Skris id = get_int(); 86265668Skris oldpath = get_string(NULL); 86365668Skris newpath = get_string(NULL); 86465668Skris TRACE("rename id %d old %s new %s", id, oldpath, newpath); 86576259Sgreen /* fail if 'newpath' exists */ 86676259Sgreen if (stat(newpath, &st) == -1) { 86776259Sgreen ret = rename(oldpath, newpath); 86876259Sgreen status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 86976259Sgreen } 87065668Skris send_status(id, status); 87165668Skris xfree(oldpath); 87265668Skris xfree(newpath); 87365668Skris} 87465668Skris 87592555Sdesstatic void 87676259Sgreenprocess_readlink(void) 87776259Sgreen{ 87876259Sgreen u_int32_t id; 87992555Sdes int len; 88076259Sgreen char link[MAXPATHLEN]; 88176259Sgreen char *path; 88265668Skris 88376259Sgreen id = get_int(); 88476259Sgreen path = get_string(NULL); 88576259Sgreen TRACE("readlink id %d path %s", id, path); 88692555Sdes if ((len = readlink(path, link, sizeof(link) - 1)) == -1) 88776259Sgreen send_status(id, errno_to_portable(errno)); 88876259Sgreen else { 88976259Sgreen Stat s; 89092555Sdes 89192555Sdes link[len] = '\0'; 89276259Sgreen attrib_clear(&s.attrib); 89376259Sgreen s.name = s.long_name = link; 89476259Sgreen send_names(id, 1, &s); 89576259Sgreen } 89676259Sgreen xfree(path); 89776259Sgreen} 89876259Sgreen 89992555Sdesstatic void 90076259Sgreenprocess_symlink(void) 90176259Sgreen{ 90276259Sgreen u_int32_t id; 90376259Sgreen struct stat st; 90476259Sgreen char *oldpath, *newpath; 90576259Sgreen int ret, status = SSH2_FX_FAILURE; 90676259Sgreen 90776259Sgreen id = get_int(); 90876259Sgreen oldpath = get_string(NULL); 90976259Sgreen newpath = get_string(NULL); 91076259Sgreen TRACE("symlink id %d old %s new %s", id, oldpath, newpath); 91176259Sgreen /* fail if 'newpath' exists */ 91276259Sgreen if (stat(newpath, &st) == -1) { 91376259Sgreen ret = symlink(oldpath, newpath); 91476259Sgreen status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 91576259Sgreen } 91676259Sgreen send_status(id, status); 91776259Sgreen xfree(oldpath); 91876259Sgreen xfree(newpath); 91976259Sgreen} 92076259Sgreen 92192555Sdesstatic void 92276259Sgreenprocess_extended(void) 92376259Sgreen{ 92476259Sgreen u_int32_t id; 92576259Sgreen char *request; 92676259Sgreen 92776259Sgreen id = get_int(); 92876259Sgreen request = get_string(NULL); 92976259Sgreen send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 93076259Sgreen xfree(request); 93176259Sgreen} 93276259Sgreen 93365668Skris/* stolen from ssh-agent */ 93465668Skris 93592555Sdesstatic void 93665668Skrisprocess(void) 93765668Skris{ 93876259Sgreen u_int msg_len; 93976259Sgreen u_int type; 94076259Sgreen u_char *cp; 94165668Skris 94265668Skris if (buffer_len(&iqueue) < 5) 94365668Skris return; /* Incomplete message. */ 94492555Sdes cp = buffer_ptr(&iqueue); 94565668Skris msg_len = GET_32BIT(cp); 94665668Skris if (msg_len > 256 * 1024) { 94765668Skris error("bad message "); 94865668Skris exit(11); 94965668Skris } 95065668Skris if (buffer_len(&iqueue) < msg_len + 4) 95165668Skris return; 95265668Skris buffer_consume(&iqueue, 4); 95365668Skris type = buffer_get_char(&iqueue); 95465668Skris switch (type) { 95576259Sgreen case SSH2_FXP_INIT: 95665668Skris process_init(); 95765668Skris break; 95876259Sgreen case SSH2_FXP_OPEN: 95965668Skris process_open(); 96065668Skris break; 96176259Sgreen case SSH2_FXP_CLOSE: 96265668Skris process_close(); 96365668Skris break; 96476259Sgreen case SSH2_FXP_READ: 96565668Skris process_read(); 96665668Skris break; 96776259Sgreen case SSH2_FXP_WRITE: 96865668Skris process_write(); 96965668Skris break; 97076259Sgreen case SSH2_FXP_LSTAT: 97165668Skris process_lstat(); 97265668Skris break; 97376259Sgreen case SSH2_FXP_FSTAT: 97465668Skris process_fstat(); 97565668Skris break; 97676259Sgreen case SSH2_FXP_SETSTAT: 97765668Skris process_setstat(); 97865668Skris break; 97976259Sgreen case SSH2_FXP_FSETSTAT: 98065668Skris process_fsetstat(); 98165668Skris break; 98276259Sgreen case SSH2_FXP_OPENDIR: 98365668Skris process_opendir(); 98465668Skris break; 98576259Sgreen case SSH2_FXP_READDIR: 98665668Skris process_readdir(); 98765668Skris break; 98876259Sgreen case SSH2_FXP_REMOVE: 98965668Skris process_remove(); 99065668Skris break; 99176259Sgreen case SSH2_FXP_MKDIR: 99265668Skris process_mkdir(); 99365668Skris break; 99476259Sgreen case SSH2_FXP_RMDIR: 99565668Skris process_rmdir(); 99665668Skris break; 99776259Sgreen case SSH2_FXP_REALPATH: 99865668Skris process_realpath(); 99965668Skris break; 100076259Sgreen case SSH2_FXP_STAT: 100165668Skris process_stat(); 100265668Skris break; 100376259Sgreen case SSH2_FXP_RENAME: 100465668Skris process_rename(); 100565668Skris break; 100676259Sgreen case SSH2_FXP_READLINK: 100776259Sgreen process_readlink(); 100876259Sgreen break; 100976259Sgreen case SSH2_FXP_SYMLINK: 101076259Sgreen process_symlink(); 101176259Sgreen break; 101276259Sgreen case SSH2_FXP_EXTENDED: 101376259Sgreen process_extended(); 101476259Sgreen break; 101565668Skris default: 101665668Skris error("Unknown message %d", type); 101765668Skris break; 101865668Skris } 101965668Skris} 102065668Skris 102165668Skrisint 102265668Skrismain(int ac, char **av) 102365668Skris{ 102476259Sgreen fd_set *rset, *wset; 102565668Skris int in, out, max; 102676259Sgreen ssize_t len, olen, set_size; 102765668Skris 102876259Sgreen /* XXX should use getopt */ 102976259Sgreen 103065668Skris handle_init(); 103165668Skris 103276259Sgreen#ifdef DEBUG_SFTP_SERVER 103376259Sgreen log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); 103476259Sgreen#endif 103576259Sgreen 103665668Skris in = dup(STDIN_FILENO); 103765668Skris out = dup(STDOUT_FILENO); 103865668Skris 103965668Skris max = 0; 104065668Skris if (in > max) 104165668Skris max = in; 104265668Skris if (out > max) 104365668Skris max = out; 104465668Skris 104565668Skris buffer_init(&iqueue); 104665668Skris buffer_init(&oqueue); 104765668Skris 104876259Sgreen set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 104976259Sgreen rset = (fd_set *)xmalloc(set_size); 105076259Sgreen wset = (fd_set *)xmalloc(set_size); 105176259Sgreen 105265668Skris for (;;) { 105376259Sgreen memset(rset, 0, set_size); 105476259Sgreen memset(wset, 0, set_size); 105565668Skris 105676259Sgreen FD_SET(in, rset); 105765668Skris olen = buffer_len(&oqueue); 105865668Skris if (olen > 0) 105976259Sgreen FD_SET(out, wset); 106065668Skris 106176259Sgreen if (select(max+1, rset, wset, NULL, NULL) < 0) { 106265668Skris if (errno == EINTR) 106365668Skris continue; 106465668Skris exit(2); 106565668Skris } 106665668Skris 106765668Skris /* copy stdin to iqueue */ 106876259Sgreen if (FD_ISSET(in, rset)) { 106965668Skris char buf[4*4096]; 107065668Skris len = read(in, buf, sizeof buf); 107165668Skris if (len == 0) { 107265668Skris debug("read eof"); 107365668Skris exit(0); 107465668Skris } else if (len < 0) { 107565668Skris error("read error"); 107665668Skris exit(1); 107765668Skris } else { 107865668Skris buffer_append(&iqueue, buf, len); 107965668Skris } 108065668Skris } 108165668Skris /* send oqueue to stdout */ 108276259Sgreen if (FD_ISSET(out, wset)) { 108365668Skris len = write(out, buffer_ptr(&oqueue), olen); 108465668Skris if (len < 0) { 108565668Skris error("write error"); 108665668Skris exit(1); 108765668Skris } else { 108865668Skris buffer_consume(&oqueue, len); 108965668Skris } 109065668Skris } 109165668Skris /* process requests from client */ 109265668Skris process(); 109365668Skris } 109465668Skris} 1095