Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mRemoteNG/PuTTYNG.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'ssh/sftpserver.c')
-rw-r--r--ssh/sftpserver.c279
1 files changed, 279 insertions, 0 deletions
diff --git a/ssh/sftpserver.c b/ssh/sftpserver.c
new file mode 100644
index 00000000..ba216872
--- /dev/null
+++ b/ssh/sftpserver.c
@@ -0,0 +1,279 @@
+/*
+ * Implement the centralised parts of the server side of SFTP.
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "putty.h"
+#include "ssh.h"
+#include "sftp.h"
+
+struct sftp_packet *sftp_handle_request(
+ SftpServer *srv, struct sftp_packet *req)
+{
+ struct sftp_packet *reply;
+ unsigned id;
+ uint32_t flags;
+ ptrlen path, dstpath, handle, data;
+ uint64_t offset;
+ unsigned length;
+ struct fxp_attrs attrs;
+ DefaultSftpReplyBuilder dsrb;
+ SftpReplyBuilder *rb;
+
+ if (req->type == SSH_FXP_INIT) {
+ /*
+ * Special case which doesn't have a request id at the start.
+ */
+ reply = sftp_pkt_init(SSH_FXP_VERSION);
+ /*
+ * Since we support only the lowest protocol version, we don't
+ * need to take the min of this and the client's version, or
+ * even to bother reading the client version number out of the
+ * input packet.
+ */
+ put_uint32(reply, SFTP_PROTO_VERSION);
+ return reply;
+ }
+
+ /*
+ * Centralise the request id handling. We'll overwrite the type
+ * code of the output packet later.
+ */
+ id = get_uint32(req);
+ reply = sftp_pkt_init(0);
+ put_uint32(reply, id);
+
+ dsrb.rb.vt = &DefaultSftpReplyBuilder_vt;
+ dsrb.pkt = reply;
+ rb = &dsrb.rb;
+
+ switch (req->type) {
+ case SSH_FXP_REALPATH:
+ path = get_string(req);
+ if (get_err(req))
+ goto decode_error;
+ sftpsrv_realpath(srv, rb, path);
+ break;
+
+ case SSH_FXP_OPEN:
+ path = get_string(req);
+ flags = get_uint32(req);
+ get_fxp_attrs(req, &attrs);
+ if (get_err(req))
+ goto decode_error;
+ if ((flags & (SSH_FXF_READ|SSH_FXF_WRITE)) == 0) {
+ fxp_reply_error(rb, SSH_FX_BAD_MESSAGE,
+ "open without READ or WRITE flag");
+ } else if ((flags & (SSH_FXF_CREAT|SSH_FXF_TRUNC)) == SSH_FXF_TRUNC) {
+ fxp_reply_error(rb, SSH_FX_BAD_MESSAGE,
+ "open with TRUNC but not CREAT");
+ } else if ((flags & (SSH_FXF_CREAT|SSH_FXF_EXCL)) == SSH_FXF_EXCL) {
+ fxp_reply_error(rb, SSH_FX_BAD_MESSAGE,
+ "open with EXCL but not CREAT");
+ } else {
+ sftpsrv_open(srv, rb, path, flags, attrs);
+ }
+ break;
+
+ case SSH_FXP_OPENDIR:
+ path = get_string(req);
+ if (get_err(req))
+ goto decode_error;
+ sftpsrv_opendir(srv, rb, path);
+ break;
+
+ case SSH_FXP_CLOSE:
+ handle = get_string(req);
+ if (get_err(req))
+ goto decode_error;
+ sftpsrv_close(srv, rb, handle);
+ break;
+
+ case SSH_FXP_MKDIR:
+ path = get_string(req);
+ get_fxp_attrs(req, &attrs);
+ if (get_err(req))
+ goto decode_error;
+ sftpsrv_mkdir(srv, rb, path, attrs);
+ break;
+
+ case SSH_FXP_RMDIR:
+ path = get_string(req);
+ if (get_err(req))
+ goto decode_error;
+ sftpsrv_rmdir(srv, rb, path);
+ break;
+
+ case SSH_FXP_REMOVE:
+ path = get_string(req);
+ if (get_err(req))
+ goto decode_error;
+ sftpsrv_remove(srv, rb, path);
+ break;
+
+ case SSH_FXP_RENAME:
+ path = get_string(req);
+ dstpath = get_string(req);
+ if (get_err(req))
+ goto decode_error;
+ sftpsrv_rename(srv, rb, path, dstpath);
+ break;
+
+ case SSH_FXP_STAT:
+ path = get_string(req);
+ if (get_err(req))
+ goto decode_error;
+ sftpsrv_stat(srv, rb, path, true);
+ break;
+
+ case SSH_FXP_LSTAT:
+ path = get_string(req);
+ if (get_err(req))
+ goto decode_error;
+ sftpsrv_stat(srv, rb, path, false);
+ break;
+
+ case SSH_FXP_FSTAT:
+ handle = get_string(req);
+ if (get_err(req))
+ goto decode_error;
+ sftpsrv_fstat(srv, rb, handle);
+ break;
+
+ case SSH_FXP_SETSTAT:
+ path = get_string(req);
+ get_fxp_attrs(req, &attrs);
+ if (get_err(req))
+ goto decode_error;
+ sftpsrv_setstat(srv, rb, path, attrs);
+ break;
+
+ case SSH_FXP_FSETSTAT:
+ handle = get_string(req);
+ get_fxp_attrs(req, &attrs);
+ if (get_err(req))
+ goto decode_error;
+ sftpsrv_fsetstat(srv, rb, handle, attrs);
+ break;
+
+ case SSH_FXP_READ:
+ handle = get_string(req);
+ offset = get_uint64(req);
+ length = get_uint32(req);
+ if (get_err(req))
+ goto decode_error;
+ sftpsrv_read(srv, rb, handle, offset, length);
+ break;
+
+ case SSH_FXP_READDIR:
+ handle = get_string(req);
+ if (get_err(req))
+ goto decode_error;
+ sftpsrv_readdir(srv, rb, handle, INT_MAX, false);
+ break;
+
+ case SSH_FXP_WRITE:
+ handle = get_string(req);
+ offset = get_uint64(req);
+ data = get_string(req);
+ if (get_err(req))
+ goto decode_error;
+ sftpsrv_write(srv, rb, handle, offset, data);
+ break;
+
+ default:
+ if (get_err(req))
+ goto decode_error;
+ fxp_reply_error(rb, SSH_FX_OP_UNSUPPORTED,
+ "Unrecognised request type");
+ break;
+
+ decode_error:
+ fxp_reply_error(rb, SSH_FX_BAD_MESSAGE, "Unable to decode request");
+ }
+
+ return reply;
+}
+
+static void default_reply_ok(SftpReplyBuilder *reply)
+{
+ DefaultSftpReplyBuilder *d =
+ container_of(reply, DefaultSftpReplyBuilder, rb);
+ d->pkt->type = SSH_FXP_STATUS;
+ put_uint32(d->pkt, SSH_FX_OK);
+ put_stringz(d->pkt, "");
+}
+
+static void default_reply_error(
+ SftpReplyBuilder *reply, unsigned code, const char *msg)
+{
+ DefaultSftpReplyBuilder *d =
+ container_of(reply, DefaultSftpReplyBuilder, rb);
+ d->pkt->type = SSH_FXP_STATUS;
+ put_uint32(d->pkt, code);
+ put_stringz(d->pkt, msg);
+}
+
+static void default_reply_name_count(SftpReplyBuilder *reply, unsigned count)
+{
+ DefaultSftpReplyBuilder *d =
+ container_of(reply, DefaultSftpReplyBuilder, rb);
+ d->pkt->type = SSH_FXP_NAME;
+ put_uint32(d->pkt, count);
+}
+
+static void default_reply_full_name(SftpReplyBuilder *reply, ptrlen name,
+ ptrlen longname, struct fxp_attrs attrs)
+{
+ DefaultSftpReplyBuilder *d =
+ container_of(reply, DefaultSftpReplyBuilder, rb);
+ d->pkt->type = SSH_FXP_NAME;
+ put_stringpl(d->pkt, name);
+ put_stringpl(d->pkt, longname);
+ put_fxp_attrs(d->pkt, attrs);
+}
+
+static void default_reply_simple_name(SftpReplyBuilder *reply, ptrlen name)
+{
+ fxp_reply_name_count(reply, 1);
+ fxp_reply_full_name(reply, name, PTRLEN_LITERAL(""), no_attrs);
+}
+
+static void default_reply_handle(SftpReplyBuilder *reply, ptrlen handle)
+{
+ DefaultSftpReplyBuilder *d =
+ container_of(reply, DefaultSftpReplyBuilder, rb);
+ d->pkt->type = SSH_FXP_HANDLE;
+ put_stringpl(d->pkt, handle);
+}
+
+static void default_reply_data(SftpReplyBuilder *reply, ptrlen data)
+{
+ DefaultSftpReplyBuilder *d =
+ container_of(reply, DefaultSftpReplyBuilder, rb);
+ d->pkt->type = SSH_FXP_DATA;
+ put_stringpl(d->pkt, data);
+}
+
+static void default_reply_attrs(
+ SftpReplyBuilder *reply, struct fxp_attrs attrs)
+{
+ DefaultSftpReplyBuilder *d =
+ container_of(reply, DefaultSftpReplyBuilder, rb);
+ d->pkt->type = SSH_FXP_ATTRS;
+ put_fxp_attrs(d->pkt, attrs);
+}
+
+const SftpReplyBuilderVtable DefaultSftpReplyBuilder_vt = {
+ .reply_ok = default_reply_ok,
+ .reply_error = default_reply_error,
+ .reply_simple_name = default_reply_simple_name,
+ .reply_name_count = default_reply_name_count,
+ .reply_full_name = default_reply_full_name,
+ .reply_handle = default_reply_handle,
+ .reply_data = default_reply_data,
+ .reply_attrs = default_reply_attrs,
+};