]> andersk Git - openssh.git/blobdiff - sftp-server.c
- markus@cvs.openbsd.org 2002/01/25 22:07:40
[openssh.git] / sftp-server.c
index 346411e798ba1771926465aa0dc0019d8f250406..6d6658614d9c1f29aa315658ade7e656f2159a30 100644 (file)
@@ -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.19 2001/02/07 18:01:18 itojun 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,7 +214,7 @@ handle_to_dir(int handle)
        return NULL;
 }
 
-int
+static int
 handle_to_fd(int handle)
 {
        if (handle_is_ok(handle, HANDLE_FILE))
@@ -212,10 +222,11 @@ handle_to_fd(int handle)
        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];
@@ -402,7 +437,7 @@ process_read(void)
        len = get_int();
 
        TRACE("read id %d handle %d off %llu len %d", id, handle,
-           (unsigned long long)off, len);
+           (u_int64_t)off, len);
        if (len > sizeof buf) {
                len = sizeof buf;
                log("read change len %d", len);
@@ -428,7 +463,7 @@ process_read(void)
                send_status(id, status);
 }
 
-void
+static void
 process_write(void)
 {
        u_int32_t id;
@@ -443,7 +478,7 @@ process_write(void)
        data = get_string(&len);
 
        TRACE("write id %d handle %d off %llu len %d", id, handle,
-           (unsigned long long)off, len);
+           (u_int64_t)off, len);
        fd = handle_to_fd(handle);
        if (fd >= 0) {
                if (lseek(fd, off, SEEK_SET) < 0) {
@@ -466,7 +501,7 @@ process_write(void)
        xfree(data);
 }
 
-void
+static void
 process_do_stat(int do_lstat)
 {
        Attrib a;
@@ -491,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;
@@ -529,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;
@@ -540,7 +576,7 @@ attrib_to_tv(Attrib *a)
        return tv;
 }
 
-void
+static void
 process_setstat(void)
 {
        Attrib *a;
@@ -572,7 +608,7 @@ process_setstat(void)
        xfree(name);
 }
 
-void
+static void
 process_fsetstat(void)
 {
        Attrib *a;
@@ -609,7 +645,11 @@ process_fsetstat(void)
                                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);
                }
@@ -617,7 +657,7 @@ process_fsetstat(void)
        send_status(id, status);
 }
 
-void
+static void
 process_opendir(void)
 {
        DIR *dirp = NULL;
@@ -649,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);
@@ -680,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 %8llu %s %s", mode,
-           st->st_nlink, user, group, (unsigned 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;
@@ -713,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));
@@ -728,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);
                        }
@@ -739,7 +782,7 @@ process_readdir(void)
        }
 }
 
-void
+static void
 process_remove(void)
 {
        char *name;
@@ -756,7 +799,7 @@ process_remove(void)
        xfree(name);
 }
 
-void
+static void
 process_mkdir(void)
 {
        Attrib *a;
@@ -776,7 +819,7 @@ process_mkdir(void)
        xfree(name);
 }
 
-void
+static void
 process_rmdir(void)
 {
        u_int32_t id;
@@ -792,7 +835,7 @@ process_rmdir(void)
        xfree(name);
 }
 
-void
+static void
 process_realpath(void)
 {
        char resolvedname[MAXPATHLEN];
@@ -817,7 +860,7 @@ process_realpath(void)
        xfree(path);
 }
 
-void
+static void
 process_rename(void)
 {
        u_int32_t id;
@@ -839,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;
@@ -853,7 +942,7 @@ process_extended(void)
 
 /* stolen from ssh-agent */
 
-void
+static void
 process(void)
 {
        u_int msg_len;
@@ -862,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 ");
@@ -924,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;
@@ -936,9 +1031,11 @@ 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();
@@ -950,6 +1047,11 @@ main(int ac, char **av)
        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;
@@ -959,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) {
@@ -989,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");
This page took 1.976151 seconds and 4 git commands to generate.