X-Git-Url: http://andersk.mit.edu/gitweb/openssh.git/blobdiff_plain/61e9624802b421b3087730b8dbb768af2021dc6d..a209a15852aa7eb8bedb2a1d595ab2c378183449:/sftp-server.c diff --git a/sftp-server.c b/sftp-server.c index 0e004009..6d665861 100644 --- a/sftp-server.c +++ b/sftp-server.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000 Markus Friedl. All rights reserved. + * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -22,7 +22,7 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "includes.h" -RCSID("$OpenBSD: sftp-server.c,v 1.15 2001/02/04 11:11:54 djm Exp $"); +RCSID("$OpenBSD: sftp-server.c,v 1.32 2001/12/29 21:56:01 stevesk Exp $"); #include "buffer.h" #include "bufaux.h" @@ -49,6 +49,9 @@ char *__progname; Buffer iqueue; Buffer oqueue; +/* Version of client */ +int version; + /* portable attibutes, etc. */ typedef struct Stat Stat; @@ -59,10 +62,11 @@ struct Stat { Attrib attrib; }; -int +static int errno_to_portable(int unixerrno) { int ret = 0; + switch (unixerrno) { case 0: ret = SSH2_FX_OK; @@ -89,12 +93,13 @@ errno_to_portable(int unixerrno) return ret; } -int +static int flags_from_portable(int pflags) { int flags = 0; - if (pflags & SSH2_FXF_READ && - pflags & SSH2_FXF_WRITE) { + + if ((pflags & SSH2_FXF_READ) && + (pflags & SSH2_FXF_WRITE)) { flags = O_RDWR; } else if (pflags & SSH2_FXF_READ) { flags = O_RDONLY; @@ -110,7 +115,7 @@ flags_from_portable(int pflags) return flags; } -Attrib * +static Attrib * get_attrib(void) { return decode_attrib(&iqueue); @@ -125,26 +130,30 @@ struct Handle { int fd; char *name; }; + enum { HANDLE_UNUSED, HANDLE_DIR, HANDLE_FILE }; + Handle handles[100]; -void +static void handle_init(void) { int i; - for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) + + for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) handles[i].use = HANDLE_UNUSED; } -int +static int handle_new(int use, char *name, int fd, DIR *dirp) { int i; - for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) { + + for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) { if (handles[i].use == HANDLE_UNUSED) { handles[i].use = use; handles[i].dirp = dirp; @@ -156,14 +165,14 @@ handle_new(int use, char *name, int fd, DIR *dirp) return -1; } -int +static int handle_is_ok(int i, int type) { return i >= 0 && i < sizeof(handles)/sizeof(Handle) && handles[i].use == type; } -int +static int handle_to_string(int handle, char **stringp, int *hlenp) { if (stringp == NULL || hlenp == NULL) @@ -174,10 +183,11 @@ handle_to_string(int handle, char **stringp, int *hlenp) return 0; } -int +static int handle_from_string(char *handle, u_int hlen) { int val; + if (hlen != sizeof(int32_t)) return -1; val = GET_32BIT(handle); @@ -187,7 +197,7 @@ handle_from_string(char *handle, u_int hlen) return -1; } -char * +static char * handle_to_name(int handle) { if (handle_is_ok(handle, HANDLE_DIR)|| @@ -196,7 +206,7 @@ handle_to_name(int handle) return NULL; } -DIR * +static DIR * handle_to_dir(int handle) { if (handle_is_ok(handle, HANDLE_DIR)) @@ -204,18 +214,19 @@ handle_to_dir(int handle) return NULL; } -int +static int handle_to_fd(int handle) { - if (handle_is_ok(handle, HANDLE_FILE)) + if (handle_is_ok(handle, HANDLE_FILE)) return handles[handle].fd; return -1; } -int +static int handle_close(int handle) { int ret = -1; + if (handle_is_ok(handle, HANDLE_FILE)) { ret = close(handles[handle].fd); handles[handle].use = HANDLE_UNUSED; @@ -228,12 +239,13 @@ handle_close(int handle) return ret; } -int +static int get_handle(void) { char *handle; int val = -1; u_int hlen; + handle = get_string(&hlen); if (hlen < 256) val = handle_from_string(handle, hlen); @@ -243,31 +255,51 @@ get_handle(void) /* send replies */ -void +static void send_msg(Buffer *m) { int mlen = buffer_len(m); + buffer_put_int(&oqueue, mlen); buffer_append(&oqueue, buffer_ptr(m), mlen); buffer_consume(m, mlen); } -void +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 */ + "No such file", /* SSH_FX_NO_SUCH_FILE */ + "Permission denied", /* SSH_FX_PERMISSION_DENIED */ + "Failure", /* SSH_FX_FAILURE */ + "Bad message", /* SSH_FX_BAD_MESSAGE */ + "No connection", /* SSH_FX_NO_CONNECTION */ + "Connection lost", /* SSH_FX_CONNECTION_LOST */ + "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ + "Unknown error" /* Others */ + }; + 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, error); + if (version >= 3) { + buffer_put_cstring(&msg, + status_messages[MIN(error,SSH2_FX_MAX)]); + buffer_put_cstring(&msg, ""); + } send_msg(&msg); buffer_free(&msg); } -void +static void send_data_or_handle(char type, u_int32_t id, char *data, int dlen) { Buffer msg; + buffer_init(&msg); buffer_put_char(&msg, type); buffer_put_int(&msg, id); @@ -276,29 +308,31 @@ send_data_or_handle(char type, u_int32_t id, char *data, int dlen) buffer_free(&msg); } -void +static void send_data(u_int32_t id, char *data, int dlen) { TRACE("sent data id %d len %d", id, dlen); send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); } -void +static void send_handle(u_int32_t id, int handle) { char *string; int hlen; + handle_to_string(handle, &string, &hlen); TRACE("sent handle id %d handle %d", id, handle); send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); xfree(string); } -void +static void send_names(u_int32_t id, int count, Stat *stats) { Buffer msg; int i; + buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_NAME); buffer_put_int(&msg, id); @@ -313,10 +347,11 @@ send_names(u_int32_t id, int count, Stat *stats) buffer_free(&msg); } -void +static void send_attrib(u_int32_t id, Attrib *a) { Buffer msg; + TRACE("sent attrib id %d have 0x%x", id, a->flags); buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_ATTRS); @@ -328,12 +363,12 @@ send_attrib(u_int32_t id, Attrib *a) /* parse incoming */ -void +static void process_init(void) { Buffer msg; - int version = buffer_get_int(&iqueue); + version = buffer_get_int(&iqueue); TRACE("client version %d", version); buffer_init(&msg); buffer_put_char(&msg, SSH2_FXP_VERSION); @@ -342,7 +377,7 @@ process_init(void) buffer_free(&msg); } -void +static void process_open(void) { u_int32_t id, pflags; @@ -374,7 +409,7 @@ process_open(void) xfree(name); } -void +static void process_close(void) { u_int32_t id; @@ -388,7 +423,7 @@ process_close(void) send_status(id, status); } -void +static void process_read(void) { char buf[64*1024]; @@ -401,7 +436,8 @@ process_read(void) off = get_int64(); len = get_int(); - TRACE("read id %d handle %d off %lld len %d", id, handle, 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; log("read change len %d", len); @@ -427,7 +463,7 @@ process_read(void) send_status(id, status); } -void +static void process_write(void) { u_int32_t id; @@ -441,7 +477,8 @@ process_write(void) off = get_int64(); data = get_string(&len); - TRACE("write id %d handle %d off %lld len %d", id, handle, 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) { @@ -464,7 +501,7 @@ process_write(void) xfree(data); } -void +static void process_do_stat(int do_lstat) { Attrib a; @@ -489,19 +526,19 @@ process_do_stat(int do_lstat) xfree(name); } -void +static void process_stat(void) { process_do_stat(0); } -void +static void process_lstat(void) { process_do_stat(1); } -void +static void process_fstat(void) { Attrib a; @@ -527,10 +564,11 @@ process_fstat(void) send_status(id, status); } -struct timeval * +static struct timeval * attrib_to_tv(Attrib *a) { static struct timeval tv[2]; + tv[0].tv_sec = a->atime; tv[0].tv_usec = 0; tv[1].tv_sec = a->mtime; @@ -538,7 +576,7 @@ attrib_to_tv(Attrib *a) return tv; } -void +static void process_setstat(void) { Attrib *a; @@ -561,11 +599,16 @@ process_setstat(void) if (ret == -1) status = errno_to_portable(errno); } + if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { + ret = chown(name, a->uid, a->gid); + if (ret == -1) + status = errno_to_portable(errno); + } send_status(id, status); xfree(name); } -void +static void process_fsetstat(void) { Attrib *a; @@ -597,6 +640,15 @@ process_fsetstat(void) ret = futimes(fd, attrib_to_tv(a)); #else ret = utimes(name, attrib_to_tv(a)); +#endif + if (ret == -1) + status = errno_to_portable(errno); + } + if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { +#ifdef HAVE_FCHOWN + ret = fchown(fd, a->uid, a->gid); +#else + ret = chown(name, a->uid, a->gid); #endif if (ret == -1) status = errno_to_portable(errno); @@ -605,7 +657,7 @@ process_fsetstat(void) send_status(id, status); } -void +static void process_opendir(void) { DIR *dirp = NULL; @@ -616,7 +668,7 @@ process_opendir(void) id = get_int(); path = get_string(NULL); TRACE("opendir id %d path %s", id, path); - dirp = opendir(path); + dirp = opendir(path); if (dirp == NULL) { status = errno_to_portable(errno); } else { @@ -627,7 +679,7 @@ process_opendir(void) send_handle(id, handle); status = SSH2_FX_OK; } - + } if (status != SSH2_FX_OK) send_status(id, status); @@ -637,10 +689,10 @@ process_opendir(void) /* * drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh */ -char * +static char * ls_file(char *name, struct stat *st) { - int sz = 0; + int ulen, glen, sz = 0; struct passwd *pw; struct group *gr; struct tm *ltime = localtime(&st->st_mtime); @@ -668,12 +720,15 @@ ls_file(char *name, struct stat *st) } if (sz == 0) tbuf[0] = '\0'; - snprintf(buf, sizeof buf, "%s %3d %-8.8s %-8.8s %8lld %s %s", mode, - st->st_nlink, user, group, (long long)st->st_size, tbuf, name); + 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); } -void +static void process_readdir(void) { DIR *dirp; @@ -701,8 +756,8 @@ process_readdir(void) stats = xrealloc(stats, nstats * sizeof(Stat)); } /* XXX OVERFLOW ? */ - snprintf(pathname, sizeof pathname, - "%s/%s", path, dp->d_name); + snprintf(pathname, sizeof pathname, "%s%s%s", path, + strcmp(path, "/") ? "/" : "", dp->d_name); if (lstat(pathname, &st) < 0) continue; stat_to_attrib(&st, &(stats[count].attrib)); @@ -716,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); } @@ -727,7 +782,7 @@ process_readdir(void) } } -void +static void process_remove(void) { char *name; @@ -744,7 +799,7 @@ process_remove(void) xfree(name); } -void +static void process_mkdir(void) { Attrib *a; @@ -764,7 +819,7 @@ process_mkdir(void) xfree(name); } -void +static void process_rmdir(void) { u_int32_t id; @@ -780,7 +835,7 @@ process_rmdir(void) xfree(name); } -void +static void process_realpath(void) { char resolvedname[MAXPATHLEN]; @@ -805,7 +860,7 @@ process_realpath(void) xfree(path); } -void +static void process_rename(void) { u_int32_t id; @@ -827,7 +882,53 @@ process_rename(void) xfree(newpath); } -void +static void +process_readlink(void) +{ + u_int32_t id; + int len; + char link[MAXPATHLEN]; + char *path; + + id = get_int(); + path = get_string(NULL); + 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; + + link[len] = '\0'; + attrib_clear(&s.attrib); + s.name = s.long_name = link; + send_names(id, 1, &s); + } + xfree(path); +} + +static void +process_symlink(void) +{ + u_int32_t id; + struct stat st; + char *oldpath, *newpath; + int ret, status = SSH2_FX_FAILURE; + + id = get_int(); + oldpath = get_string(NULL); + newpath = get_string(NULL); + 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(void) { u_int32_t id; @@ -841,7 +942,7 @@ process_extended(void) /* stolen from ssh-agent */ -void +static void process(void) { u_int msg_len; @@ -850,7 +951,7 @@ process(void) if (buffer_len(&iqueue) < 5) return; /* Incomplete message. */ - cp = (u_char *) buffer_ptr(&iqueue); + cp = buffer_ptr(&iqueue); msg_len = GET_32BIT(cp); if (msg_len > 256 * 1024) { error("bad message "); @@ -912,6 +1013,12 @@ process(void) case SSH2_FXP_RENAME: process_rename(); break; + case SSH2_FXP_READLINK: + process_readlink(); + break; + case SSH2_FXP_SYMLINK: + process_symlink(); + break; case SSH2_FXP_EXTENDED: process_extended(); break; @@ -924,20 +1031,27 @@ process(void) int main(int ac, char **av) { - fd_set rset, wset; + fd_set *rset, *wset; int in, out, max; - ssize_t len, olen; + ssize_t len, olen, set_size; + + /* XXX should use getopt */ __progname = get_progname(av[0]); handle_init(); #ifdef DEBUG_SFTP_SERVER - log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); + log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); #endif in = dup(STDIN_FILENO); out = dup(STDOUT_FILENO); +#ifdef HAVE_CYGWIN + setmode(in, O_BINARY); + setmode(out, O_BINARY); +#endif + max = 0; if (in > max) max = in; @@ -947,23 +1061,27 @@ main(int ac, char **av) buffer_init(&iqueue); buffer_init(&oqueue); + set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); + rset = (fd_set *)xmalloc(set_size); + wset = (fd_set *)xmalloc(set_size); + for (;;) { - FD_ZERO(&rset); - FD_ZERO(&wset); + memset(rset, 0, set_size); + memset(wset, 0, set_size); - FD_SET(in, &rset); + FD_SET(in, rset); olen = buffer_len(&oqueue); if (olen > 0) - FD_SET(out, &wset); + FD_SET(out, wset); - if (select(max+1, &rset, &wset, NULL, NULL) < 0) { + if (select(max+1, rset, wset, NULL, NULL) < 0) { if (errno == EINTR) continue; exit(2); } /* copy stdin to iqueue */ - if (FD_ISSET(in, &rset)) { + if (FD_ISSET(in, rset)) { char buf[4*4096]; len = read(in, buf, sizeof buf); if (len == 0) { @@ -977,7 +1095,7 @@ main(int ac, char **av) } } /* send oqueue to stdout */ - if (FD_ISSET(out, &wset)) { + if (FD_ISSET(out, wset)) { len = write(out, buffer_ptr(&oqueue), olen); if (len < 0) { error("write error");