X-Git-Url: http://andersk.mit.edu/gitweb/gssapi-openssh.git/blobdiff_plain/476861787f6e1b8a6c6af9307a15b9e57cb979dc..4e1c687b22bf2e4eb8d3549081a1f468eb13e374:/openssh/sftp-server.c diff --git a/openssh/sftp-server.c b/openssh/sftp-server.c index d9549f5..2ef9753 100644 --- a/openssh/sftp-server.c +++ b/openssh/sftp-server.c @@ -1,46 +1,34 @@ -/* $OpenBSD: sftp-server.c,v 1.78 2008/02/27 20:21:15 djm Exp $ */ /* - * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. + * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ - #include "includes.h" +RCSID("$OpenBSD: sftp-server.c,v 1.30 2001/07/31 12:42:50 jakob Exp $"); -#include -#include -#include -#ifdef HAVE_SYS_TIME_H -# include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "xmalloc.h" #include "buffer.h" +#include "bufaux.h" +#include "getput.h" #include "log.h" -#include "misc.h" -#include "uidswap.h" +#include "xmalloc.h" #include "sftp.h" #include "sftp-common.h" @@ -49,13 +37,13 @@ #define get_int64() buffer_get_int64(&iqueue); #define get_int() buffer_get_int(&iqueue); #define get_string(lenp) buffer_get_string(&iqueue, lenp); +#define TRACE debug -/* Our verbosity */ -LogLevel log_level = SYSLOG_LEVEL_ERROR; - -/* Our client */ -struct passwd *pw = NULL; -char *client_addr = NULL; +#ifdef HAVE___PROGNAME +extern char *__progname; +#else +char *__progname; +#endif /* input and output queue */ Buffer iqueue; @@ -64,7 +52,7 @@ Buffer oqueue; /* Version of client */ int version; -/* portable attributes, etc. */ +/* portable attibutes, etc. */ typedef struct Stat Stat; @@ -127,33 +115,6 @@ flags_from_portable(int pflags) return flags; } -static const char * -string_from_portable(int pflags) -{ - static char ret[128]; - - *ret = '\0'; - -#define PAPPEND(str) { \ - if (*ret != '\0') \ - strlcat(ret, ",", sizeof(ret)); \ - strlcat(ret, str, sizeof(ret)); \ - } - - if (pflags & SSH2_FXF_READ) - PAPPEND("READ") - if (pflags & SSH2_FXF_WRITE) - PAPPEND("WRITE") - if (pflags & SSH2_FXF_CREAT) - PAPPEND("CREATE") - if (pflags & SSH2_FXF_TRUNC) - PAPPEND("TRUNCATE") - if (pflags & SSH2_FXF_EXCL) - PAPPEND("EXCL") - - return ret; -} - static Attrib * get_attrib(void) { @@ -168,8 +129,6 @@ struct Handle { DIR *dirp; int fd; char *name; - u_int64_t bytes_read, bytes_write; - int next_unused; }; enum { @@ -178,46 +137,39 @@ enum { HANDLE_FILE }; -Handle *handles = NULL; -u_int num_handles = 0; -int first_unused_handle = -1; +Handle handles[100]; -static void handle_unused(int i) +static void +handle_init(void) { - handles[i].use = HANDLE_UNUSED; - handles[i].next_unused = first_unused_handle; - first_unused_handle = i; + int i; + + for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) + handles[i].use = HANDLE_UNUSED; } static int -handle_new(int use, const char *name, int fd, DIR *dirp) +handle_new(int use, char *name, int fd, DIR *dirp) { int i; - if (first_unused_handle == -1) { - if (num_handles + 1 <= num_handles) - return -1; - num_handles++; - handles = xrealloc(handles, num_handles, sizeof(Handle)); - handle_unused(num_handles - 1); + for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) { + if (handles[i].use == HANDLE_UNUSED) { + handles[i].use = use; + handles[i].dirp = dirp; + handles[i].fd = fd; + handles[i].name = name; + return i; + } } - - i = first_unused_handle; - first_unused_handle = handles[i].next_unused; - - handles[i].use = use; - handles[i].dirp = dirp; - handles[i].fd = fd; - handles[i].name = xstrdup(name); - handles[i].bytes_read = handles[i].bytes_write = 0; - - return i; + return -1; } static int handle_is_ok(int i, int type) { - return i >= 0 && (u_int)i < num_handles && handles[i].use == type; + return i >= 0 && i < sizeof(handles)/sizeof(Handle) && + handles[i].use == type; } static int @@ -226,19 +178,19 @@ handle_to_string(int handle, char **stringp, int *hlenp) if (stringp == NULL || hlenp == NULL) return -1; *stringp = xmalloc(sizeof(int32_t)); - put_u32(*stringp, handle); + PUT_32BIT(*stringp, handle); *hlenp = sizeof(int32_t); return 0; } static int -handle_from_string(const char *handle, u_int hlen) +handle_from_string(char *handle, u_int hlen) { int val; if (hlen != sizeof(int32_t)) return -1; - val = get_u32(handle); + val = GET_32BIT(handle); if (handle_is_ok(val, HANDLE_FILE) || handle_is_ok(val, HANDLE_DIR)) return val; @@ -270,36 +222,6 @@ handle_to_fd(int handle) return -1; } -static void -handle_update_read(int handle, ssize_t bytes) -{ - if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) - handles[handle].bytes_read += bytes; -} - -static void -handle_update_write(int handle, ssize_t bytes) -{ - if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) - handles[handle].bytes_write += bytes; -} - -static u_int64_t -handle_bytes_read(int handle) -{ - if (handle_is_ok(handle, HANDLE_FILE)) - return (handles[handle].bytes_read); - return 0; -} - -static u_int64_t -handle_bytes_write(int handle) -{ - if (handle_is_ok(handle, HANDLE_FILE)) - return (handles[handle].bytes_write); - return 0; -} - static int handle_close(int handle) { @@ -307,44 +229,16 @@ handle_close(int handle) if (handle_is_ok(handle, HANDLE_FILE)) { ret = close(handles[handle].fd); - xfree(handles[handle].name); - handle_unused(handle); + handles[handle].use = HANDLE_UNUSED; } else if (handle_is_ok(handle, HANDLE_DIR)) { ret = closedir(handles[handle].dirp); - xfree(handles[handle].name); - handle_unused(handle); + handles[handle].use = HANDLE_UNUSED; } else { errno = ENOENT; } return ret; } -static void -handle_log_close(int handle, char *emsg) -{ - if (handle_is_ok(handle, HANDLE_FILE)) { - logit("%s%sclose \"%s\" bytes read %llu written %llu", - emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", - handle_to_name(handle), - (unsigned long long)handle_bytes_read(handle), - (unsigned long long)handle_bytes_write(handle)); - } else { - logit("%s%sclosedir \"%s\"", - emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", - handle_to_name(handle)); - } -} - -static void -handle_log_exit(void) -{ - u_int i; - - for (i = 0; i < num_handles; i++) - if (handles[i].use != HANDLE_UNUSED) - handle_log_close(i, "forced"); -} - static int get_handle(void) { @@ -371,9 +265,10 @@ send_msg(Buffer *m) buffer_consume(m, mlen); } -static const char * -status_to_message(u_int32_t status) +static void +send_status(u_int32_t id, u_int32_t error) { + Buffer msg; const char *status_messages[] = { "Success", /* SSH_FX_OK */ "End of file", /* SSH_FX_EOF */ @@ -386,31 +281,22 @@ status_to_message(u_int32_t status) "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ "Unknown error" /* Others */ }; - return (status_messages[MIN(status,SSH2_FX_MAX)]); -} -static void -send_status(u_int32_t id, u_int32_t status) -{ - Buffer msg; - - debug3("request %u: sent status %u", id, status); - if (log_level > SYSLOG_LEVEL_VERBOSE || - (status != SSH2_FX_OK && status != SSH2_FX_EOF)) - logit("sent status %s", status_to_message(status)); + TRACE("sent status id %d error %d", id, error); buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_STATUS); buffer_put_int(&msg, id); - buffer_put_int(&msg, status); + buffer_put_int(&msg, error); if (version >= 3) { - buffer_put_cstring(&msg, status_to_message(status)); + buffer_put_cstring(&msg, + status_messages[MIN(error,SSH2_FX_MAX)]); buffer_put_cstring(&msg, ""); } send_msg(&msg); buffer_free(&msg); } static void -send_data_or_handle(char type, u_int32_t id, const char *data, int dlen) +send_data_or_handle(char type, u_int32_t id, char *data, int dlen) { Buffer msg; @@ -423,9 +309,9 @@ send_data_or_handle(char type, u_int32_t id, const char *data, int dlen) } static void -send_data(u_int32_t id, const char *data, int dlen) +send_data(u_int32_t id, char *data, int dlen) { - debug("request %u: sent data len %d", id, dlen); + TRACE("sent data id %d len %d", id, dlen); send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); } @@ -436,13 +322,13 @@ send_handle(u_int32_t id, int handle) int hlen; handle_to_string(handle, &string, &hlen); - debug("request %u: sent handle handle %d", id, handle); + TRACE("sent handle id %d handle %d", id, handle); send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); xfree(string); } static void -send_names(u_int32_t id, int count, const Stat *stats) +send_names(u_int32_t id, int count, Stat *stats) { Buffer msg; int i; @@ -451,7 +337,7 @@ send_names(u_int32_t id, int count, const Stat *stats) buffer_put_char(&msg, SSH2_FXP_NAME); buffer_put_int(&msg, id); buffer_put_int(&msg, count); - debug("request %u: sent names count %d", id, count); + TRACE("sent names id %d count %d", id, count); for (i = 0; i < count; i++) { buffer_put_cstring(&msg, stats[i].name); buffer_put_cstring(&msg, stats[i].long_name); @@ -462,11 +348,11 @@ send_names(u_int32_t id, int count, const Stat *stats) } static void -send_attrib(u_int32_t id, const Attrib *a) +send_attrib(u_int32_t id, Attrib *a) { Buffer msg; - debug("request %u: sent attrib have 0x%x", id, a->flags); + TRACE("sent attrib id %d have 0x%x", id, a->flags); buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_ATTRS); buffer_put_int(&msg, id); @@ -482,14 +368,11 @@ process_init(void) { Buffer msg; - version = get_int(); - verbose("received client version %d", version); + version = buffer_get_int(&iqueue); + TRACE("client version %d", version); buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_VERSION); buffer_put_int(&msg, SSH2_FILEXFER_VERSION); - /* POSIX rename extension */ - buffer_put_cstring(&msg, "posix-rename@openssh.com"); - buffer_put_cstring(&msg, "1"); /* version */ send_msg(&msg); buffer_free(&msg); } @@ -505,17 +388,15 @@ process_open(void) id = get_int(); name = get_string(NULL); pflags = get_int(); /* portable flags */ - debug3("request %u: open flags %d", id, pflags); a = get_attrib(); flags = flags_from_portable(pflags); mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; - logit("open \"%s\" flags %s mode 0%o", - name, string_from_portable(pflags), mode); + TRACE("open id %d name %s flags %d mode 0%o", id, name, pflags, mode); fd = open(name, flags, mode); if (fd < 0) { status = errno_to_portable(errno); } else { - handle = handle_new(HANDLE_FILE, name, fd, NULL); + handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL); if (handle < 0) { close(fd); } else { @@ -536,8 +417,7 @@ process_close(void) id = get_int(); handle = get_handle(); - debug3("request %u: close handle %u", id, handle); - handle_log_close(handle, NULL); + TRACE("close id %d handle %d", id, handle); ret = handle_close(handle); status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; send_status(id, status); @@ -556,11 +436,11 @@ process_read(void) off = get_int64(); len = get_int(); - debug("request %u: read \"%s\" (handle %d) off %llu len %d", - id, handle_to_name(handle), handle, (unsigned long long)off, len); + TRACE("read id %d handle %d off %llu len %d", id, handle, + (u_int64_t)off, len); if (len > sizeof buf) { len = sizeof buf; - debug2("read change len %d", len); + log("read change len %d", len); } fd = handle_to_fd(handle); if (fd >= 0) { @@ -576,7 +456,6 @@ process_read(void) } else { send_data(id, buf, ret); status = SSH2_FX_OK; - handle_update_read(handle, ret); } } } @@ -598,8 +477,8 @@ process_write(void) off = get_int64(); data = get_string(&len); - debug("request %u: write \"%s\" (handle %d) off %llu len %d", - id, handle_to_name(handle), handle, (unsigned long long)off, len); + TRACE("write id %d handle %d off %llu len %d", id, handle, + (u_int64_t)off, len); fd = handle_to_fd(handle); if (fd >= 0) { if (lseek(fd, off, SEEK_SET) < 0) { @@ -608,14 +487,13 @@ process_write(void) } else { /* XXX ATOMICIO ? */ ret = write(fd, data, len); - if (ret < 0) { + if (ret == -1) { error("process_write: write failed"); status = errno_to_portable(errno); - } else if ((size_t)ret == len) { + } else if (ret == len) { status = SSH2_FX_OK; - handle_update_write(handle, ret); } else { - debug2("nothing at all written"); + log("nothing at all written"); } } } @@ -634,8 +512,7 @@ process_do_stat(int do_lstat) id = get_int(); name = get_string(NULL); - debug3("request %u: %sstat", id, do_lstat ? "l" : ""); - verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); + TRACE("%sstat id %d name %s", do_lstat ? "l" : "", id, name); ret = do_lstat ? lstat(name, &st) : stat(name, &st); if (ret < 0) { status = errno_to_portable(errno); @@ -671,10 +548,9 @@ process_fstat(void) id = get_int(); handle = get_handle(); - debug("request %u: fstat \"%s\" (handle %u)", - id, handle_to_name(handle), handle); + TRACE("fstat id %d handle %d", id, handle); fd = handle_to_fd(handle); - if (fd >= 0) { + if (fd >= 0) { ret = fstat(fd, &st); if (ret < 0) { status = errno_to_portable(errno); @@ -689,7 +565,7 @@ process_fstat(void) } static struct timeval * -attrib_to_tv(const Attrib *a) +attrib_to_tv(Attrib *a) { static struct timeval tv[2]; @@ -706,39 +582,24 @@ process_setstat(void) Attrib *a; u_int32_t id; char *name; - int status = SSH2_FX_OK, ret; + int ret; + int status = SSH2_FX_OK; id = get_int(); name = get_string(NULL); a = get_attrib(); - debug("request %u: setstat name \"%s\"", id, name); - if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { - logit("set \"%s\" size %llu", - name, (unsigned long long)a->size); - ret = truncate(name, a->size); - if (ret == -1) - status = errno_to_portable(errno); - } + TRACE("setstat id %d name %s", id, name); if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { - logit("set \"%s\" mode %04o", name, a->perm); ret = chmod(name, a->perm & 0777); if (ret == -1) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { - char buf[64]; - time_t t = a->mtime; - - strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", - localtime(&t)); - logit("set \"%s\" modtime %s", name, buf); ret = utimes(name, attrib_to_tv(a)); if (ret == -1) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { - logit("set \"%s\" owner %lu group %lu", name, - (u_long)a->uid, (u_long)a->gid); ret = chown(name, a->uid, a->gid); if (ret == -1) status = errno_to_portable(errno); @@ -754,26 +615,18 @@ process_fsetstat(void) u_int32_t id; int handle, fd, ret; int status = SSH2_FX_OK; + char *name; id = get_int(); handle = get_handle(); a = get_attrib(); - debug("request %u: fsetstat handle %d", id, handle); + TRACE("fsetstat id %d handle %d", id, handle); fd = handle_to_fd(handle); - if (fd < 0) { + name = handle_to_name(handle); + if (fd < 0 || name == NULL) { status = SSH2_FX_FAILURE; } else { - char *name = handle_to_name(handle); - - if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { - logit("set \"%s\" size %llu", - name, (unsigned long long)a->size); - ret = ftruncate(fd, a->size); - if (ret == -1) - status = errno_to_portable(errno); - } if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { - logit("set \"%s\" mode %04o", name, a->perm); #ifdef HAVE_FCHMOD ret = fchmod(fd, a->perm & 0777); #else @@ -783,12 +636,6 @@ process_fsetstat(void) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { - char buf[64]; - time_t t = a->mtime; - - strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", - localtime(&t)); - logit("set \"%s\" modtime %s", name, buf); #ifdef HAVE_FUTIMES ret = futimes(fd, attrib_to_tv(a)); #else @@ -798,8 +645,6 @@ process_fsetstat(void) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { - logit("set \"%s\" owner %lu group %lu", name, - (u_long)a->uid, (u_long)a->gid); #ifdef HAVE_FCHOWN ret = fchown(fd, a->uid, a->gid); #else @@ -822,13 +667,12 @@ process_opendir(void) id = get_int(); path = get_string(NULL); - debug3("request %u: opendir", id); - logit("opendir \"%s\"", path); + TRACE("opendir id %d path %s", id, path); dirp = opendir(path); if (dirp == NULL) { status = errno_to_portable(errno); } else { - handle = handle_new(HANDLE_DIR, path, 0, dirp); + handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp); if (handle < 0) { closedir(dirp); } else { @@ -842,6 +686,48 @@ process_opendir(void) xfree(path); } +/* + * drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh + */ +static char * +ls_file(char *name, struct stat *st) +{ + int ulen, glen, sz = 0; + struct passwd *pw; + struct group *gr; + struct tm *ltime = localtime(&st->st_mtime); + char *user, *group; + char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1]; + + strmode(st->st_mode, mode); + if ((pw = getpwuid(st->st_uid)) != NULL) { + user = pw->pw_name; + } else { + snprintf(ubuf, sizeof ubuf, "%d", st->st_uid); + user = ubuf; + } + if ((gr = getgrgid(st->st_gid)) != NULL) { + group = gr->gr_name; + } else { + snprintf(gbuf, sizeof gbuf, "%d", st->st_gid); + group = gbuf; + } + if (ltime != NULL) { + if (time(NULL) - st->st_mtime < (365*24*60*60)/2) + sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime); + else + sz = strftime(tbuf, sizeof tbuf, "%b %e %Y", ltime); + } + if (sz == 0) + tbuf[0] = '\0'; + ulen = MAX(strlen(user), 8); + glen = MAX(strlen(group), 8); + snprintf(buf, sizeof buf, "%s %3d %-*s %-*s %8llu %s %s", mode, + st->st_nlink, ulen, user, glen, group, + (u_int64_t)st->st_size, tbuf, name); + return xstrdup(buf); +} + static void process_readdir(void) { @@ -853,23 +739,21 @@ process_readdir(void) id = get_int(); handle = get_handle(); - debug("request %u: readdir \"%s\" (handle %d)", id, - handle_to_name(handle), handle); + TRACE("readdir id %d handle %d", id, handle); dirp = handle_to_dir(handle); path = handle_to_name(handle); if (dirp == NULL || path == NULL) { send_status(id, SSH2_FX_FAILURE); } else { struct stat st; - char pathname[MAXPATHLEN]; + char pathname[1024]; Stat *stats; int nstats = 10, count = 0, i; - - stats = xcalloc(nstats, sizeof(Stat)); + stats = xmalloc(nstats * sizeof(Stat)); while ((dp = readdir(dirp)) != NULL) { if (count >= nstats) { nstats *= 2; - stats = xrealloc(stats, nstats, sizeof(Stat)); + stats = xrealloc(stats, nstats * sizeof(Stat)); } /* XXX OVERFLOW ? */ snprintf(pathname, sizeof pathname, "%s%s%s", path, @@ -878,7 +762,7 @@ process_readdir(void) continue; stat_to_attrib(&st, &(stats[count].attrib)); stats[count].name = xstrdup(dp->d_name); - stats[count].long_name = ls_file(dp->d_name, &st, 0); + stats[count].long_name = ls_file(dp->d_name, &st); count++; /* send up to 100 entries in one message */ /* XXX check packet size instead */ @@ -887,7 +771,7 @@ process_readdir(void) } if (count > 0) { send_names(id, count, stats); - for (i = 0; i < count; i++) { + for(i = 0; i < count; i++) { xfree(stats[i].name); xfree(stats[i].long_name); } @@ -908,8 +792,7 @@ process_remove(void) id = get_int(); name = get_string(NULL); - debug3("request %u: remove", id); - logit("remove name \"%s\"", name); + TRACE("remove id %d name %s", id, name); ret = unlink(name); status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; send_status(id, status); @@ -929,8 +812,7 @@ process_mkdir(void) a = get_attrib(); mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm & 0777 : 0777; - debug3("request %u: mkdir", id); - logit("mkdir name \"%s\" mode 0%o", name, mode); + TRACE("mkdir id %d name %s mode 0%o", id, name, mode); ret = mkdir(name, mode); status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; send_status(id, status); @@ -946,8 +828,7 @@ process_rmdir(void) id = get_int(); name = get_string(NULL); - debug3("request %u: rmdir", id); - logit("rmdir name \"%s\"", name); + TRACE("rmdir id %d name %s", id, name); ret = rmdir(name); status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; send_status(id, status); @@ -967,8 +848,7 @@ process_realpath(void) xfree(path); path = xstrdup("."); } - debug3("request %u: realpath", id); - verbose("realpath \"%s\"", path); + TRACE("realpath id %d path %s", id, path); if (realpath(path, resolvedname) == NULL) { send_status(id, errno_to_portable(errno)); } else { @@ -984,53 +864,18 @@ static void process_rename(void) { u_int32_t id; + struct stat st; char *oldpath, *newpath; - int status; - struct stat sb; + int ret, status = SSH2_FX_FAILURE; id = get_int(); oldpath = get_string(NULL); newpath = get_string(NULL); - debug3("request %u: rename", id); - logit("rename old \"%s\" new \"%s\"", oldpath, newpath); - status = SSH2_FX_FAILURE; - if (lstat(oldpath, &sb) == -1) - status = errno_to_portable(errno); - else if (S_ISREG(sb.st_mode)) { - /* Race-free rename of regular files */ - if (link(oldpath, newpath) == -1) { - if (errno == EOPNOTSUPP -#ifdef LINK_OPNOTSUPP_ERRNO - || errno == LINK_OPNOTSUPP_ERRNO -#endif - ) { - struct stat st; - - /* - * fs doesn't support links, so fall back to - * stat+rename. This is racy. - */ - if (stat(newpath, &st) == -1) { - if (rename(oldpath, newpath) == -1) - status = - errno_to_portable(errno); - else - status = SSH2_FX_OK; - } - } else { - status = errno_to_portable(errno); - } - } else if (unlink(oldpath) == -1) { - status = errno_to_portable(errno); - /* clean spare link */ - unlink(newpath); - } else - status = SSH2_FX_OK; - } else if (stat(newpath, &sb) == -1) { - if (rename(oldpath, newpath) == -1) - status = errno_to_portable(errno); - else - status = SSH2_FX_OK; + TRACE("rename id %d old %s new %s", id, oldpath, newpath); + /* fail if 'newpath' exists */ + if (stat(newpath, &st) == -1) { + ret = rename(oldpath, newpath); + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; } send_status(id, status); xfree(oldpath); @@ -1042,21 +887,20 @@ process_readlink(void) { u_int32_t id; int len; - char buf[MAXPATHLEN]; + char link[MAXPATHLEN]; char *path; id = get_int(); path = get_string(NULL); - debug3("request %u: readlink", id); - verbose("readlink \"%s\"", path); - if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) + TRACE("readlink id %d path %s", id, path); + if ((len = readlink(path, link, sizeof(link) - 1)) == -1) send_status(id, errno_to_portable(errno)); else { Stat s; - - buf[len] = '\0'; + + link[len] = '\0'; attrib_clear(&s.attrib); - s.name = s.long_name = buf; + s.name = s.long_name = link; send_names(id, 1, &s); } xfree(path); @@ -1066,39 +910,24 @@ static void process_symlink(void) { u_int32_t id; + struct stat st; char *oldpath, *newpath; - int ret, status; + int ret, status = SSH2_FX_FAILURE; id = get_int(); oldpath = get_string(NULL); newpath = get_string(NULL); - debug3("request %u: symlink", id); - logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); - /* this will fail if 'newpath' exists */ - ret = symlink(oldpath, newpath); - status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + TRACE("symlink id %d old %s new %s", id, oldpath, newpath); + /* fail if 'newpath' exists */ + if (stat(newpath, &st) == -1) { + ret = symlink(oldpath, newpath); + status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; + } send_status(id, status); xfree(oldpath); xfree(newpath); } -static void -process_extended_posix_rename(u_int32_t id) -{ - char *oldpath, *newpath; - - oldpath = get_string(NULL); - newpath = get_string(NULL); - debug3("request %u: posix-rename", id); - logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); - if (rename(oldpath, newpath) == -1) - send_status(id, errno_to_portable(errno)); - else - send_status(id, SSH2_FX_OK); - xfree(oldpath); - xfree(newpath); -} - static void process_extended(void) { @@ -1107,10 +936,7 @@ process_extended(void) id = get_int(); request = get_string(NULL); - if (strcmp(request, "posix-rename@openssh.com") == 0) - process_extended_posix_rename(id); - else - send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ + send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ xfree(request); } @@ -1120,25 +946,20 @@ static void process(void) { u_int msg_len; - u_int buf_len; - u_int consumed; u_int type; u_char *cp; - buf_len = buffer_len(&iqueue); - if (buf_len < 5) + if (buffer_len(&iqueue) < 5) return; /* Incomplete message. */ - cp = buffer_ptr(&iqueue); - msg_len = get_u32(cp); - if (msg_len > SFTP_MAX_MSG_LENGTH) { - error("bad message from %s local user %s", - client_addr, pw->pw_name); - sftp_server_cleanup_exit(11); + cp = (u_char *) buffer_ptr(&iqueue); + msg_len = GET_32BIT(cp); + if (msg_len > 256 * 1024) { + error("bad message "); + exit(11); } - if (buf_len < msg_len + 4) + if (buffer_len(&iqueue) < msg_len + 4) return; buffer_consume(&iqueue, 4); - buf_len -= 4; type = buffer_get_char(&iqueue); switch (type) { case SSH2_FXP_INIT: @@ -1205,102 +1026,23 @@ process(void) error("Unknown message %d", type); break; } - /* discard the remaining bytes from the current packet */ - if (buf_len < buffer_len(&iqueue)) { - error("iqueue grew unexpectedly"); - sftp_server_cleanup_exit(255); - } - consumed = buf_len - buffer_len(&iqueue); - if (msg_len < consumed) { - error("msg_len %d < consumed %d", msg_len, consumed); - sftp_server_cleanup_exit(255); - } - if (msg_len > consumed) - buffer_consume(&iqueue, msg_len - consumed); -} - -/* Cleanup handler that logs active handles upon normal exit */ -void -sftp_server_cleanup_exit(int i) -{ - if (pw != NULL && client_addr != NULL) { - handle_log_exit(); - logit("session closed for local user %s from [%s]", - pw->pw_name, client_addr); - } - _exit(i); -} - -static void -sftp_server_usage(void) -{ - extern char *__progname; - - fprintf(stderr, - "usage: %s [-he] [-l log_level] [-f log_facility]\n", __progname); - exit(1); } int -sftp_server_main(int argc, char **argv, struct passwd *user_pw) +main(int ac, char **av) { fd_set *rset, *wset; - int in, out, max, ch, skipargs = 0, log_stderr = 0; + int in, out, max; ssize_t len, olen, set_size; - SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; - char *cp, buf[4*4096]; - - extern char *optarg; - extern char *__progname; - - __progname = ssh_get_progname(argv[0]); - log_init(__progname, log_level, log_facility, log_stderr); - - while (!skipargs && (ch = getopt(argc, argv, "C:f:l:che")) != -1) { - switch (ch) { - case 'c': - /* - * Ignore all arguments if we are invoked as a - * shell using "sftp-server -c command" - */ - skipargs = 1; - break; - case 'e': - log_stderr = 1; - break; - case 'l': - log_level = log_level_number(optarg); - if (log_level == SYSLOG_LEVEL_NOT_SET) - error("Invalid log level \"%s\"", optarg); - break; - case 'f': - log_facility = log_facility_number(optarg); - if (log_facility == SYSLOG_FACILITY_NOT_SET) - error("Invalid log facility \"%s\"", optarg); - break; - case 'h': - default: - sftp_server_usage(); - } - } - - log_init(__progname, log_level, log_facility, log_stderr); - if ((cp = getenv("SSH_CONNECTION")) != NULL) { - client_addr = xstrdup(cp); - if ((cp = strchr(client_addr, ' ')) == NULL) { - error("Malformed SSH_CONNECTION variable: \"%s\"", - getenv("SSH_CONNECTION")); - sftp_server_cleanup_exit(255); - } - *cp = '\0'; - } else - client_addr = xstrdup("UNKNOWN"); + /* XXX should use getopt */ - pw = pwcopy(user_pw); + __progname = get_progname(av[0]); + handle_init(); - logit("session opened for local user %s from [%s]", - pw->pw_name, client_addr); +#ifdef DEBUG_SFTP_SERVER + log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); +#endif in = dup(STDIN_FILENO); out = dup(STDOUT_FILENO); @@ -1327,15 +1069,7 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) memset(rset, 0, set_size); memset(wset, 0, set_size); - /* - * Ensure that we can read a full buffer and handle - * the worst-case length packet it can generate, - * otherwise apply backpressure by stopping reads. - */ - if (buffer_check_alloc(&iqueue, sizeof(buf)) && - buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) - FD_SET(in, rset); - + FD_SET(in, rset); olen = buffer_len(&oqueue); if (olen > 0) FD_SET(out, wset); @@ -1343,19 +1077,19 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) if (select(max+1, rset, wset, NULL, NULL) < 0) { if (errno == EINTR) continue; - error("select: %s", strerror(errno)); - sftp_server_cleanup_exit(2); + exit(2); } /* copy stdin to iqueue */ if (FD_ISSET(in, rset)) { + char buf[4*4096]; len = read(in, buf, sizeof buf); if (len == 0) { debug("read eof"); - sftp_server_cleanup_exit(0); + exit(0); } else if (len < 0) { - error("read: %s", strerror(errno)); - sftp_server_cleanup_exit(1); + error("read error"); + exit(1); } else { buffer_append(&iqueue, buf, len); } @@ -1364,19 +1098,13 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) if (FD_ISSET(out, wset)) { len = write(out, buffer_ptr(&oqueue), olen); if (len < 0) { - error("write: %s", strerror(errno)); - sftp_server_cleanup_exit(1); + error("write error"); + exit(1); } else { buffer_consume(&oqueue, len); } } - - /* - * Process requests from client if we can fit the results - * into the output buffer, otherwise stop processing input - * and let the output queue drain. - */ - if (buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) - process(); + /* process requests from client */ + process(); } }