=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/sftp-server.c,v retrieving revision 1.48.2.2 retrieving revision 1.49 diff -u -r1.48.2.2 -r1.49 --- src/usr.bin/ssh/sftp-server.c 2006/10/06 03:19:33 1.48.2.2 +++ src/usr.bin/ssh/sftp-server.c 2005/09/13 23:40:07 1.49 @@ -1,4 +1,3 @@ -/* $OpenBSD: sftp-server.c,v 1.48.2.2 2006/10/06 03:19:33 brad Exp $ */ /* * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. * @@ -14,28 +13,15 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include "includes.h" +RCSID("$OpenBSD: sftp-server.c,v 1.49 2005/09/13 23:40:07 djm Exp $"); -#include -#include -#include -#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 "xmalloc.h" #include "misc.h" -#include "uidswap.h" #include "sftp.h" #include "sftp-common.h" @@ -44,14 +30,8 @@ #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; - /* input and output queue */ Buffer iqueue; Buffer oqueue; @@ -122,33 +102,6 @@ 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) { @@ -163,7 +116,6 @@ DIR *dirp; int fd; char *name; - u_int64_t bytes_read, bytes_write; }; enum { @@ -194,7 +146,6 @@ handles[i].dirp = dirp; handles[i].fd = fd; handles[i].name = xstrdup(name); - handles[i].bytes_read = handles[i].bytes_write = 0; return i; } } @@ -214,7 +165,7 @@ 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; } @@ -226,7 +177,7 @@ 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; @@ -258,36 +209,6 @@ 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,31 +228,6 @@ 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), - handle_bytes_read(handle), 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 < sizeof(handles)/sizeof(Handle); i++) - if (handles[i].use != HANDLE_UNUSED) - handle_log_close(i, "forced"); -} - static int get_handle(void) { @@ -358,9 +254,10 @@ 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 status) { + Buffer msg; const char *status_messages[] = { "Success", /* SSH_FX_OK */ "End of file", /* SSH_FX_EOF */ @@ -373,24 +270,15 @@ "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 %u error %u", id, status); buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_STATUS); buffer_put_int(&msg, id); buffer_put_int(&msg, status); if (version >= 3) { - buffer_put_cstring(&msg, status_to_message(status)); + buffer_put_cstring(&msg, + status_messages[MIN(status,SSH2_FX_MAX)]); buffer_put_cstring(&msg, ""); } send_msg(&msg); @@ -412,7 +300,7 @@ static void send_data(u_int32_t id, const char *data, int dlen) { - debug("request %u: sent data len %d", id, dlen); + TRACE("sent data id %u len %d", id, dlen); send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); } @@ -423,7 +311,7 @@ int hlen; handle_to_string(handle, &string, &hlen); - debug("request %u: sent handle handle %d", id, handle); + TRACE("sent handle id %u handle %d", id, handle); send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); xfree(string); } @@ -438,7 +326,7 @@ 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 %u 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); @@ -453,7 +341,7 @@ { Buffer msg; - debug("request %u: sent attrib have 0x%x", id, a->flags); + TRACE("sent attrib id %u have 0x%x", id, a->flags); buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_ATTRS); buffer_put_int(&msg, id); @@ -470,7 +358,7 @@ Buffer msg; version = get_int(); - verbose("received client version %d", version); + TRACE("client version %d", version); buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_VERSION); buffer_put_int(&msg, SSH2_FILEXFER_VERSION); @@ -489,12 +377,10 @@ 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 %u name %s flags %d mode 0%o", id, name, pflags, mode); fd = open(name, flags, mode); if (fd < 0) { status = errno_to_portable(errno); @@ -520,8 +406,7 @@ id = get_int(); handle = get_handle(); - debug3("request %u: close handle %u", id, handle); - handle_log_close(handle, NULL); + TRACE("close id %u handle %d", id, handle); ret = handle_close(handle); status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; send_status(id, status); @@ -540,11 +425,11 @@ 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 %u handle %d off %llu len %d", id, handle, + (unsigned long long)off, len); if (len > sizeof buf) { len = sizeof buf; - debug2("read change len %d", len); + logit("read change len %d", len); } fd = handle_to_fd(handle); if (fd >= 0) { @@ -560,7 +445,6 @@ } else { send_data(id, buf, ret); status = SSH2_FX_OK; - handle_update_read(handle, ret); } } } @@ -582,8 +466,8 @@ 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 %u handle %d off %llu len %d", id, handle, + (unsigned long long)off, len); fd = handle_to_fd(handle); if (fd >= 0) { if (lseek(fd, off, SEEK_SET) < 0) { @@ -597,9 +481,8 @@ status = errno_to_portable(errno); } else if ((size_t)ret == len) { status = SSH2_FX_OK; - handle_update_write(handle, ret); } else { - debug2("nothing at all written"); + logit("nothing at all written"); } } } @@ -618,8 +501,7 @@ 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 %u name %s", do_lstat ? "l" : "", id, name); ret = do_lstat ? lstat(name, &st) : stat(name, &st); if (ret < 0) { status = errno_to_portable(errno); @@ -655,8 +537,7 @@ id = get_int(); handle = get_handle(); - debug("request %u: fstat \"%s\" (handle %u)", - id, handle_to_name(handle), handle); + TRACE("fstat id %u handle %d", id, handle); fd = handle_to_fd(handle); if (fd >= 0) { ret = fstat(fd, &st); @@ -695,33 +576,23 @@ id = get_int(); name = get_string(NULL); a = get_attrib(); - debug("request %u: setstat name \"%s\"", id, name); + TRACE("setstat id %u name %s", id, name); if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { - logit("set \"%s\" size %llu", name, a->size); ret = truncate(name, 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); 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); @@ -741,39 +612,27 @@ id = get_int(); handle = get_handle(); a = get_attrib(); - debug("request %u: fsetstat handle %d", id, handle); + TRACE("fsetstat id %u handle %d", id, handle); fd = handle_to_fd(handle); if (fd < 0) { status = SSH2_FX_FAILURE; } else { - char *name = handle_to_name(handle); - if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { - logit("set \"%s\" size %llu", name, 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); ret = fchmod(fd, 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 = futimes(fd, 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 = fchown(fd, a->uid, a->gid); if (ret == -1) status = errno_to_portable(errno); @@ -792,8 +651,7 @@ id = get_int(); path = get_string(NULL); - debug3("request %u: opendir", id); - logit("opendir \"%s\"", path); + TRACE("opendir id %u path %s", id, path); dirp = opendir(path); if (dirp == NULL) { status = errno_to_portable(errno); @@ -823,23 +681,22 @@ id = get_int(); handle = get_handle(); - debug("request %u: readdir \"%s\" (handle %d)", id, - handle_to_name(handle), handle); + TRACE("readdir id %u 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,8 +735,7 @@ id = get_int(); name = get_string(NULL); - debug3("request %u: remove", id); - logit("remove name \"%s\"", name); + TRACE("remove id %u name %s", id, name); ret = unlink(name); status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; send_status(id, status); @@ -899,8 +755,7 @@ 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 %u 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); @@ -916,8 +771,7 @@ id = get_int(); name = get_string(NULL); - debug3("request %u: rmdir", id); - logit("rmdir name \"%s\"", name); + TRACE("rmdir id %u name %s", id, name); ret = rmdir(name); status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; send_status(id, status); @@ -937,8 +791,7 @@ xfree(path); path = xstrdup("."); } - debug3("request %u: realpath", id); - verbose("realpath \"%s\"", path); + TRACE("realpath id %u path %s", id, path); if (realpath(path, resolvedname) == NULL) { send_status(id, errno_to_portable(errno)); } else { @@ -961,8 +814,7 @@ id = get_int(); oldpath = get_string(NULL); newpath = get_string(NULL); - debug3("request %u: rename", id); - logit("rename old \"%s\" new \"%s\"", oldpath, newpath); + TRACE("rename id %u old %s new %s", id, oldpath, newpath); status = SSH2_FX_FAILURE; if (lstat(oldpath, &sb) == -1) status = errno_to_portable(errno); @@ -1013,8 +865,7 @@ id = get_int(); path = get_string(NULL); - debug3("request %u: readlink", id); - verbose("readlink \"%s\"", path); + TRACE("readlink id %u path %s", id, path); if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) send_status(id, errno_to_portable(errno)); else { @@ -1038,8 +889,7 @@ id = get_int(); oldpath = get_string(NULL); newpath = get_string(NULL); - debug3("request %u: symlink", id); - logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); + TRACE("symlink id %u old %s new %s", id, oldpath, newpath); /* this will fail if 'newpath' exists */ ret = symlink(oldpath, newpath); status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; @@ -1075,11 +925,10 @@ if (buf_len < 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); - cleanup_exit(11); + msg_len = GET_32BIT(cp); + if (msg_len > 256 * 1024) { + error("bad message "); + exit(11); } if (buf_len < msg_len + 4) return; @@ -1153,7 +1002,7 @@ } /* discard the remaining bytes from the current packet */ if (buf_len < buffer_len(&iqueue)) - fatal("iqueue grew unexpectedly"); + fatal("iqueue grows"); consumed = buf_len - buffer_len(&iqueue); if (msg_len < consumed) fatal("msg_len %d < consumed %d", msg_len, consumed); @@ -1161,93 +1010,24 @@ buffer_consume(&iqueue, msg_len - consumed); } -/* Cleanup handler that logs active handles upon normal exit */ -void -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 -usage(void) -{ - extern char *__progname; - - fprintf(stderr, - "usage: %s [-he] [-l log_level] [-f log_facility]\n", __progname); - exit(1); -} - int -main(int argc, char **argv) +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; - extern char *optarg; - extern char *__progname; - /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ sanitise_stdfd(); - log_init(__progname, log_level, log_facility, log_stderr); + /* XXX should use getopt */ - 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_level == SYSLOG_FACILITY_NOT_SET) - error("Invalid log facility \"%s\"", optarg); - break; - case 'h': - default: - 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) - fatal("Malformed SSH_CONNECTION variable: \"%s\"", - getenv("SSH_CONNECTION")); - *cp = '\0'; - } else - client_addr = xstrdup("UNKNOWN"); - - if ((pw = getpwuid(getuid())) == NULL) - fatal("No user found for uid %lu", (u_long)getuid()); - pw = pwcopy(pw); - - logit("session opened for local user %s from [%s]", - pw->pw_name, client_addr); - handle_init(); +#ifdef DEBUG_SFTP_SERVER + log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); +#endif + in = dup(STDIN_FILENO); out = dup(STDOUT_FILENO); @@ -1276,8 +1056,7 @@ if (select(max+1, rset, wset, NULL, NULL) < 0) { if (errno == EINTR) continue; - error("select: %s", strerror(errno)); - cleanup_exit(2); + exit(2); } /* copy stdin to iqueue */ @@ -1286,10 +1065,10 @@ len = read(in, buf, sizeof buf); if (len == 0) { debug("read eof"); - cleanup_exit(0); + exit(0); } else if (len < 0) { - error("read: %s", strerror(errno)); - cleanup_exit(1); + error("read error"); + exit(1); } else { buffer_append(&iqueue, buf, len); } @@ -1298,8 +1077,8 @@ if (FD_ISSET(out, wset)) { len = write(out, buffer_ptr(&oqueue), olen); if (len < 0) { - error("write: %s", strerror(errno)); - cleanup_exit(1); + error("write error"); + exit(1); } else { buffer_consume(&oqueue, len); }