* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "includes.h"
-RCSID("$OpenBSD: sftp-server.c,v 1.14 2001/01/21 19:05:56 markus Exp $");
+RCSID("$OpenBSD: sftp-server.c,v 1.24 2001/03/14 22:50:25 deraadt Exp $");
#include "buffer.h"
#include "bufaux.h"
#include "xmalloc.h"
#include "sftp.h"
+#include "sftp-common.h"
/* helper */
#define get_int64() buffer_get_int64(&iqueue);
Buffer iqueue;
Buffer oqueue;
+/* Version of client */
+int version;
+
/* portable attibutes, etc. */
-typedef struct Attrib Attrib;
typedef struct Stat Stat;
-struct Attrib
-{
- u_int32_t flags;
- u_int64_t size;
- u_int32_t uid;
- u_int32_t gid;
- u_int32_t perm;
- u_int32_t atime;
- u_int32_t mtime;
-};
-
-struct Stat
-{
+struct Stat {
char *name;
char *long_name;
Attrib attrib;
errno_to_portable(int unixerrno)
{
int ret = 0;
+
switch (unixerrno) {
case 0:
ret = SSH2_FX_OK;
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;
return flags;
}
-void
-attrib_clear(Attrib *a)
-{
- a->flags = 0;
- a->size = 0;
- a->uid = 0;
- a->gid = 0;
- a->perm = 0;
- a->atime = 0;
- a->mtime = 0;
-}
-
-Attrib *
-decode_attrib(Buffer *b)
-{
- static Attrib a;
- attrib_clear(&a);
- a.flags = buffer_get_int(b);
- if (a.flags & SSH2_FILEXFER_ATTR_SIZE) {
- a.size = buffer_get_int64(b);
- }
- if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) {
- a.uid = buffer_get_int(b);
- a.gid = buffer_get_int(b);
- }
- if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
- a.perm = buffer_get_int(b);
- }
- if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
- a.atime = buffer_get_int(b);
- a.mtime = buffer_get_int(b);
- }
- /* vendor-specific extensions */
- if (a.flags & SSH2_FILEXFER_ATTR_EXTENDED) {
- char *type, *data;
- int i, count;
- count = buffer_get_int(b);
- for (i = 0; i < count; i++) {
- type = buffer_get_string(b, NULL);
- data = buffer_get_string(b, NULL);
- xfree(type);
- xfree(data);
- }
- }
- return &a;
-}
-
-void
-encode_attrib(Buffer *b, Attrib *a)
-{
- buffer_put_int(b, a->flags);
- if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
- buffer_put_int64(b, a->size);
- }
- if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
- buffer_put_int(b, a->uid);
- buffer_put_int(b, a->gid);
- }
- if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
- buffer_put_int(b, a->perm);
- }
- if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
- buffer_put_int(b, a->atime);
- buffer_put_int(b, a->mtime);
- }
-}
-
-void
-stat_to_attrib(struct stat *st, Attrib *a)
-{
- attrib_clear(a);
- a->flags = 0;
- a->flags |= SSH2_FILEXFER_ATTR_SIZE;
- a->size = st->st_size;
- a->flags |= SSH2_FILEXFER_ATTR_UIDGID;
- a->uid = st->st_uid;
- a->gid = st->st_gid;
- a->flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
- a->perm = st->st_mode;
- a->flags |= SSH2_FILEXFER_ATTR_ACMODTIME;
- a->atime = st->st_atime;
- a->mtime = st->st_mtime;
-}
-
Attrib *
get_attrib(void)
{
int fd;
char *name;
};
+
enum {
HANDLE_UNUSED,
HANDLE_DIR,
HANDLE_FILE
};
+
Handle handles[100];
void
handle_init(void)
{
int i;
+
for(i = 0; i < sizeof(handles)/sizeof(Handle); i++)
handles[i].use = HANDLE_UNUSED;
}
handle_new(int use, char *name, int fd, DIR *dirp)
{
int i;
+
for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
if (handles[i].use == HANDLE_UNUSED) {
handles[i].use = use;
handle_from_string(char *handle, u_int hlen)
{
int val;
+
if (hlen != sizeof(int32_t))
return -1;
val = GET_32BIT(handle);
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;
}
handle_close(int handle)
{
int ret = -1;
+
if (handle_is_ok(handle, HANDLE_FILE)) {
ret = close(handles[handle].fd);
handles[handle].use = HANDLE_UNUSED;
char *handle;
int val = -1;
u_int hlen;
+
handle = get_string(&hlen);
if (hlen < 256)
val = handle_from_string(handle, hlen);
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);
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);
}
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);
{
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);
{
Buffer msg;
int i;
+
buffer_init(&msg);
buffer_put_char(&msg, SSH2_FXP_NAME);
buffer_put_int(&msg, id);
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);
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);
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);
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) {
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;
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);
}
int handle, fd, ret;
int status = SSH2_FX_OK;
char *name;
-
+
id = get_int();
handle = get_handle();
a = get_attrib();
TRACE("fsetstat id %d handle %d", id, handle);
fd = handle_to_fd(handle);
name = handle_to_name(handle);
- if ((fd < 0) || (name == NULL)) {
+ if (fd < 0 || name == NULL) {
status = SSH2_FX_FAILURE;
} else {
if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
#ifdef HAVE_FCHMOD
ret = fchmod(fd, a->perm & 0777);
#else
- ret = chmod(name, a->perm & 077);
+ ret = chmod(name, a->perm & 0777);
#endif
if (ret == -1)
status = errno_to_portable(errno);
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);
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 {
send_handle(id, handle);
status = SSH2_FX_OK;
}
-
+
}
if (status != SSH2_FX_OK)
send_status(id, status);
}
if (sz == 0)
tbuf[0] = '\0';
- snprintf(buf, sizeof buf, "%s %3d %-8.8s %-8.8s %8qd %s %s", mode,
- st->st_nlink, user, group, (long long)st->st_size, tbuf, name);
+ snprintf(buf, sizeof buf, "%s %3d %-8.8s %-8.8s %8llu %s %s", mode,
+ st->st_nlink, user, group, (u_int64_t)st->st_size, tbuf, name);
return xstrdup(buf);
}
xfree(newpath);
}
+void
+process_readlink(void)
+{
+ u_int32_t id;
+ char link[MAXPATHLEN];
+ char *path;
+
+ id = get_int();
+ path = get_string(NULL);
+ TRACE("readlink id %d path %s", id, path);
+ if (readlink(path, link, sizeof(link) - 1) == -1)
+ send_status(id, errno_to_portable(errno));
+ else {
+ Stat s;
+
+ link[sizeof(link) - 1] = '\0';
+ attrib_clear(&s.attrib);
+ s.name = s.long_name = link;
+ send_names(id, 1, &s);
+ }
+ xfree(path);
+}
+
+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);
+}
+
void
process_extended(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;
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);
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) {
}
}
/* 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");