external/openssh: update to 6.8p1.

In preparation for some updates to external/openssh to make it work with
BoringSSL, this change updates the code to a recent version. The current
version (5.9p1) is coming up on four years old now.

  * Confirmed that f5c67b478bef9992de9e9ec91ce10af4f6205e0d matches
    OpenSSH 5.9p1 exactly (save for the removal of the scard
    subdirectory).

  * Downloaded openssh-6.8p1.tar.gz (SHA256:
    3ff64ce73ee124480b5bf767b9830d7d3c03bbcb6abe716b78f0192c37ce160e)
    and verified with PGP signature. (I've verified Damien's key in
    person previously.)

  * Applied changes between f5c67b478bef9992de9e9ec91ce10af4f6205e0d and
    OpenSSH 5.9p1 to 6.8p1 and updated the build as best I can. The
    ugliest change is probably the duplication of umac.c to umac128.c
    because Android conditionally compiles that file twice. See the
    comment in those files.

Change-Id: I63cb07a8118afb5a377f116087a0882914cea486
diff --git a/sftp-server.c b/sftp-server.c
index 9d01c7d..4f735cd 100644
--- a/sftp-server.c
+++ b/sftp-server.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sftp-server.c,v 1.94 2011/06/17 21:46:16 djm Exp $ */
+/* $OpenBSD: sftp-server.c,v 1.105 2015/01/20 23:14:00 deraadt Exp $ */
 /*
  * Copyright (c) 2000-2004 Markus Friedl.  All rights reserved.
  *
@@ -17,8 +17,8 @@
 
 #include "includes.h"
 
+#include <sys/param.h>	/* MIN */
 #include <sys/types.h>
-#include <sys/param.h>
 #include <sys/stat.h>
 #ifdef HAVE_SYS_TIME_H
 # include <sys/time.h>
@@ -29,6 +29,9 @@
 #ifdef HAVE_SYS_STATVFS_H
 #include <sys/statvfs.h>
 #endif
+#ifdef HAVE_SYS_PRCTL_H
+#include <sys/prctl.h>
+#endif
 
 #include <dirent.h>
 #include <errno.h>
@@ -43,38 +46,40 @@
 #include <stdarg.h>
 
 #include "xmalloc.h"
-#include "buffer.h"
+#include "sshbuf.h"
+#include "ssherr.h"
 #include "log.h"
 #include "misc.h"
+#include "match.h"
 #include "uidswap.h"
 
 #include "sftp.h"
 #include "sftp-common.h"
 
-/* helper */
-#define get_int64()			buffer_get_int64(&iqueue);
-#define get_int()			buffer_get_int(&iqueue);
-#define get_string(lenp)		buffer_get_string(&iqueue, lenp);
-
 /* Our verbosity */
-LogLevel log_level = SYSLOG_LEVEL_ERROR;
+static LogLevel log_level = SYSLOG_LEVEL_ERROR;
 
 /* Our client */
-struct passwd *pw = NULL;
-char *client_addr = NULL;
+static struct passwd *pw = NULL;
+static char *client_addr = NULL;
 
 /* input and output queue */
-Buffer iqueue;
-Buffer oqueue;
+struct sshbuf *iqueue;
+struct sshbuf *oqueue;
 
 /* Version of client */
-u_int version;
+static u_int version;
+
+/* SSH2_FXP_INIT received */
+static int init_done;
 
 /* Disable writes */
-int readonly;
+static int readonly;
+
+/* Requests that are allowed/denied */
+static char *request_whitelist, *request_blacklist;
 
 /* portable attributes, etc. */
-
 typedef struct Stat Stat;
 
 struct Stat {
@@ -83,6 +88,102 @@
 	Attrib attrib;
 };
 
+/* Packet handlers */
+static void process_open(u_int32_t id);
+static void process_close(u_int32_t id);
+static void process_read(u_int32_t id);
+static void process_write(u_int32_t id);
+static void process_stat(u_int32_t id);
+static void process_lstat(u_int32_t id);
+static void process_fstat(u_int32_t id);
+static void process_setstat(u_int32_t id);
+static void process_fsetstat(u_int32_t id);
+static void process_opendir(u_int32_t id);
+static void process_readdir(u_int32_t id);
+static void process_remove(u_int32_t id);
+static void process_mkdir(u_int32_t id);
+static void process_rmdir(u_int32_t id);
+static void process_realpath(u_int32_t id);
+static void process_rename(u_int32_t id);
+static void process_readlink(u_int32_t id);
+static void process_symlink(u_int32_t id);
+static void process_extended_posix_rename(u_int32_t id);
+static void process_extended_statvfs(u_int32_t id);
+static void process_extended_fstatvfs(u_int32_t id);
+static void process_extended_hardlink(u_int32_t id);
+static void process_extended_fsync(u_int32_t id);
+static void process_extended(u_int32_t id);
+
+struct sftp_handler {
+	const char *name;	/* user-visible name for fine-grained perms */
+	const char *ext_name;	/* extended request name */
+	u_int type;		/* packet type, for non extended packets */
+	void (*handler)(u_int32_t);
+	int does_write;		/* if nonzero, banned for readonly mode */
+};
+
+struct sftp_handler handlers[] = {
+	/* NB. SSH2_FXP_OPEN does the readonly check in the handler itself */
+	{ "open", NULL, SSH2_FXP_OPEN, process_open, 0 },
+	{ "close", NULL, SSH2_FXP_CLOSE, process_close, 0 },
+	{ "read", NULL, SSH2_FXP_READ, process_read, 0 },
+	{ "write", NULL, SSH2_FXP_WRITE, process_write, 1 },
+	{ "lstat", NULL, SSH2_FXP_LSTAT, process_lstat, 0 },
+	{ "fstat", NULL, SSH2_FXP_FSTAT, process_fstat, 0 },
+	{ "setstat", NULL, SSH2_FXP_SETSTAT, process_setstat, 1 },
+	{ "fsetstat", NULL, SSH2_FXP_FSETSTAT, process_fsetstat, 1 },
+	{ "opendir", NULL, SSH2_FXP_OPENDIR, process_opendir, 0 },
+	{ "readdir", NULL, SSH2_FXP_READDIR, process_readdir, 0 },
+	{ "remove", NULL, SSH2_FXP_REMOVE, process_remove, 1 },
+	{ "mkdir", NULL, SSH2_FXP_MKDIR, process_mkdir, 1 },
+	{ "rmdir", NULL, SSH2_FXP_RMDIR, process_rmdir, 1 },
+	{ "realpath", NULL, SSH2_FXP_REALPATH, process_realpath, 0 },
+	{ "stat", NULL, SSH2_FXP_STAT, process_stat, 0 },
+	{ "rename", NULL, SSH2_FXP_RENAME, process_rename, 1 },
+	{ "readlink", NULL, SSH2_FXP_READLINK, process_readlink, 0 },
+	{ "symlink", NULL, SSH2_FXP_SYMLINK, process_symlink, 1 },
+	{ NULL, NULL, 0, NULL, 0 }
+};
+
+/* SSH2_FXP_EXTENDED submessages */
+struct sftp_handler extended_handlers[] = {
+	{ "posix-rename", "posix-rename@openssh.com", 0,
+	   process_extended_posix_rename, 1 },
+	{ "statvfs", "statvfs@openssh.com", 0, process_extended_statvfs, 0 },
+	{ "fstatvfs", "fstatvfs@openssh.com", 0, process_extended_fstatvfs, 0 },
+	{ "hardlink", "hardlink@openssh.com", 0, process_extended_hardlink, 1 },
+	{ "fsync", "fsync@openssh.com", 0, process_extended_fsync, 1 },
+	{ NULL, NULL, 0, NULL, 0 }
+};
+
+static int
+request_permitted(struct sftp_handler *h)
+{
+	char *result;
+
+	if (readonly && h->does_write) {
+		verbose("Refusing %s request in read-only mode", h->name);
+		return 0;
+	}
+	if (request_blacklist != NULL &&
+	    ((result = match_list(h->name, request_blacklist, NULL))) != NULL) {
+		free(result);
+		verbose("Refusing blacklisted %s request", h->name);
+		return 0;
+	}
+	if (request_whitelist != NULL &&
+	    ((result = match_list(h->name, request_whitelist, NULL))) != NULL) {
+		free(result);
+		debug2("Permitting whitelisted %s request", h->name);
+		return 1;
+	}
+	if (request_whitelist != NULL) {
+		verbose("Refusing non-whitelisted %s request", h->name);
+		return 0;
+	}
+	return 1;
+}
+
 static int
 errno_to_portable(int unixerrno)
 {
@@ -130,6 +231,8 @@
 	} else if (pflags & SSH2_FXF_WRITE) {
 		flags = O_WRONLY;
 	}
+	if (pflags & SSH2_FXF_APPEND)
+		flags |= O_APPEND;
 	if (pflags & SSH2_FXF_CREAT)
 		flags |= O_CREAT;
 	if (pflags & SSH2_FXF_TRUNC)
@@ -156,6 +259,8 @@
 		PAPPEND("READ")
 	if (pflags & SSH2_FXF_WRITE)
 		PAPPEND("WRITE")
+	if (pflags & SSH2_FXF_APPEND)
+		PAPPEND("APPEND")
 	if (pflags & SSH2_FXF_CREAT)
 		PAPPEND("CREATE")
 	if (pflags & SSH2_FXF_TRUNC)
@@ -166,12 +271,6 @@
 	return ret;
 }
 
-static Attrib *
-get_attrib(void)
-{
-	return decode_attrib(&iqueue);
-}
-
 /* handle handles */
 
 typedef struct Handle Handle;
@@ -179,6 +278,7 @@
 	int use;
 	DIR *dirp;
 	int fd;
+	int flags;
 	char *name;
 	u_int64_t bytes_read, bytes_write;
 	int next_unused;
@@ -202,7 +302,7 @@
 }
 
 static int
-handle_new(int use, const char *name, int fd, DIR *dirp)
+handle_new(int use, const char *name, int fd, int flags, DIR *dirp)
 {
 	int i;
 
@@ -220,6 +320,7 @@
 	handles[i].use = use;
 	handles[i].dirp = dirp;
 	handles[i].fd = fd;
+	handles[i].flags = flags;
 	handles[i].name = xstrdup(name);
 	handles[i].bytes_read = handles[i].bytes_write = 0;
 
@@ -233,7 +334,7 @@
 }
 
 static int
-handle_to_string(int handle, char **stringp, int *hlenp)
+handle_to_string(int handle, u_char **stringp, int *hlenp)
 {
 	if (stringp == NULL || hlenp == NULL)
 		return -1;
@@ -244,7 +345,7 @@
 }
 
 static int
-handle_from_string(const char *handle, u_int hlen)
+handle_from_string(const u_char *handle, u_int hlen)
 {
 	int val;
 
@@ -282,6 +383,14 @@
 	return -1;
 }
 
+static int
+handle_to_flags(int handle)
+{
+	if (handle_is_ok(handle, HANDLE_FILE))
+		return handles[handle].flags;
+	return 0;
+}
+
 static void
 handle_update_read(int handle, ssize_t bytes)
 {
@@ -319,11 +428,11 @@
 
 	if (handle_is_ok(handle, HANDLE_FILE)) {
 		ret = close(handles[handle].fd);
-		xfree(handles[handle].name);
+		free(handles[handle].name);
 		handle_unused(handle);
 	} else if (handle_is_ok(handle, HANDLE_DIR)) {
 		ret = closedir(handles[handle].dirp);
-		xfree(handles[handle].name);
+		free(handles[handle].name);
 		handle_unused(handle);
 	} else {
 		errno = ENOENT;
@@ -358,29 +467,31 @@
 }
 
 static int
-get_handle(void)
+get_handle(struct sshbuf *queue, int *hp)
 {
-	char *handle;
-	int val = -1;
-	u_int hlen;
+	u_char *handle;
+	int r;
+	size_t hlen;
 
-	handle = get_string(&hlen);
+	*hp = -1;
+	if ((r = sshbuf_get_string(queue, &handle, &hlen)) != 0)
+		return r;
 	if (hlen < 256)
-		val = handle_from_string(handle, hlen);
-	xfree(handle);
-	return val;
+		*hp = handle_from_string(handle, hlen);
+	free(handle);
+	return 0;
 }
 
 /* send replies */
 
 static void
-send_msg(Buffer *m)
+send_msg(struct sshbuf *m)
 {
-	int mlen = buffer_len(m);
+	int r;
 
-	buffer_put_int(&oqueue, mlen);
-	buffer_append(&oqueue, buffer_ptr(m), mlen);
-	buffer_consume(m, mlen);
+	if ((r = sshbuf_put_stringb(oqueue, m)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+	sshbuf_reset(m);
 }
 
 static const char *
@@ -404,38 +515,46 @@
 static void
 send_status(u_int32_t id, u_int32_t status)
 {
-	Buffer msg;
+	struct sshbuf *msg;
+	int r;
 
 	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));
-	buffer_init(&msg);
-	buffer_put_char(&msg, SSH2_FXP_STATUS);
-	buffer_put_int(&msg, id);
-	buffer_put_int(&msg, status);
+	if ((msg = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new failed", __func__);
+	if ((r = sshbuf_put_u8(msg, SSH2_FXP_STATUS)) != 0 ||
+	    (r = sshbuf_put_u32(msg, id)) != 0 ||
+	    (r = sshbuf_put_u32(msg, status)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
 	if (version >= 3) {
-		buffer_put_cstring(&msg, status_to_message(status));
-		buffer_put_cstring(&msg, "");
+		if ((r = sshbuf_put_cstring(msg,
+		    status_to_message(status))) != 0 ||
+		    (r = sshbuf_put_cstring(msg, "")) != 0)
+			fatal("%s: buffer error: %s", __func__, ssh_err(r));
 	}
-	send_msg(&msg);
-	buffer_free(&msg);
+	send_msg(msg);
+	sshbuf_free(msg);
 }
 static void
-send_data_or_handle(char type, u_int32_t id, const char *data, int dlen)
+send_data_or_handle(char type, u_int32_t id, const u_char *data, int dlen)
 {
-	Buffer msg;
+	struct sshbuf *msg;
+	int r;
 
-	buffer_init(&msg);
-	buffer_put_char(&msg, type);
-	buffer_put_int(&msg, id);
-	buffer_put_string(&msg, data, dlen);
-	send_msg(&msg);
-	buffer_free(&msg);
+	if ((msg = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new failed", __func__);
+	if ((r = sshbuf_put_u8(msg, type)) != 0 ||
+	    (r = sshbuf_put_u32(msg, id)) != 0 ||
+	    (r = sshbuf_put_string(msg, data, dlen)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+	send_msg(msg);
+	sshbuf_free(msg);
 }
 
 static void
-send_data(u_int32_t id, const char *data, int dlen)
+send_data(u_int32_t id, const u_char *data, int dlen)
 {
 	debug("request %u: sent data len %d", id, dlen);
 	send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
@@ -444,74 +563,83 @@
 static void
 send_handle(u_int32_t id, int handle)
 {
-	char *string;
+	u_char *string;
 	int hlen;
 
 	handle_to_string(handle, &string, &hlen);
 	debug("request %u: sent handle handle %d", id, handle);
 	send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
-	xfree(string);
+	free(string);
 }
 
 static void
 send_names(u_int32_t id, int count, const Stat *stats)
 {
-	Buffer msg;
-	int i;
+	struct sshbuf *msg;
+	int i, r;
 
-	buffer_init(&msg);
-	buffer_put_char(&msg, SSH2_FXP_NAME);
-	buffer_put_int(&msg, id);
-	buffer_put_int(&msg, count);
+	if ((msg = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new failed", __func__);
+	if ((r = sshbuf_put_u8(msg, SSH2_FXP_NAME)) != 0 ||
+	    (r = sshbuf_put_u32(msg, id)) != 0 ||
+	    (r = sshbuf_put_u32(msg, count)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
 	debug("request %u: sent names 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);
-		encode_attrib(&msg, &stats[i].attrib);
+		if ((r = sshbuf_put_cstring(msg, stats[i].name)) != 0 ||
+		    (r = sshbuf_put_cstring(msg, stats[i].long_name)) != 0 ||
+		    (r = encode_attrib(msg, &stats[i].attrib)) != 0)
+			fatal("%s: buffer error: %s", __func__, ssh_err(r));
 	}
-	send_msg(&msg);
-	buffer_free(&msg);
+	send_msg(msg);
+	sshbuf_free(msg);
 }
 
 static void
 send_attrib(u_int32_t id, const Attrib *a)
 {
-	Buffer msg;
+	struct sshbuf *msg;
+	int r;
 
 	debug("request %u: sent attrib have 0x%x", id, a->flags);
-	buffer_init(&msg);
-	buffer_put_char(&msg, SSH2_FXP_ATTRS);
-	buffer_put_int(&msg, id);
-	encode_attrib(&msg, a);
-	send_msg(&msg);
-	buffer_free(&msg);
+	if ((msg = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new failed", __func__);
+	if ((r = sshbuf_put_u8(msg, SSH2_FXP_ATTRS)) != 0 ||
+	    (r = sshbuf_put_u32(msg, id)) != 0 ||
+	    (r = encode_attrib(msg, a)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+	send_msg(msg);
+	sshbuf_free(msg);
 }
 
 static void
 send_statvfs(u_int32_t id, struct statvfs *st)
 {
-	Buffer msg;
+	struct sshbuf *msg;
 	u_int64_t flag;
+	int r;
 
 	flag = (st->f_flag & ST_RDONLY) ? SSH2_FXE_STATVFS_ST_RDONLY : 0;
 	flag |= (st->f_flag & ST_NOSUID) ? SSH2_FXE_STATVFS_ST_NOSUID : 0;
 
-	buffer_init(&msg);
-	buffer_put_char(&msg, SSH2_FXP_EXTENDED_REPLY);
-	buffer_put_int(&msg, id);
-	buffer_put_int64(&msg, st->f_bsize);
-	buffer_put_int64(&msg, st->f_frsize);
-	buffer_put_int64(&msg, st->f_blocks);
-	buffer_put_int64(&msg, st->f_bfree);
-	buffer_put_int64(&msg, st->f_bavail);
-	buffer_put_int64(&msg, st->f_files);
-	buffer_put_int64(&msg, st->f_ffree);
-	buffer_put_int64(&msg, st->f_favail);
-	buffer_put_int64(&msg, FSID_TO_ULONG(st->f_fsid));
-	buffer_put_int64(&msg, flag);
-	buffer_put_int64(&msg, st->f_namemax);
-	send_msg(&msg);
-	buffer_free(&msg);
+	if ((msg = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new failed", __func__);
+	if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED_REPLY)) != 0 ||
+	    (r = sshbuf_put_u32(msg, id)) != 0 ||
+	    (r = sshbuf_put_u64(msg, st->f_bsize)) != 0 ||
+	    (r = sshbuf_put_u64(msg, st->f_frsize)) != 0 ||
+	    (r = sshbuf_put_u64(msg, st->f_blocks)) != 0 ||
+	    (r = sshbuf_put_u64(msg, st->f_bfree)) != 0 ||
+	    (r = sshbuf_put_u64(msg, st->f_bavail)) != 0 ||
+	    (r = sshbuf_put_u64(msg, st->f_files)) != 0 ||
+	    (r = sshbuf_put_u64(msg, st->f_ffree)) != 0 ||
+	    (r = sshbuf_put_u64(msg, st->f_favail)) != 0 ||
+	    (r = sshbuf_put_u64(msg, FSID_TO_ULONG(st->f_fsid))) != 0 ||
+	    (r = sshbuf_put_u64(msg, flag)) != 0 ||
+	    (r = sshbuf_put_u64(msg, st->f_namemax)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+	send_msg(msg);
+	sshbuf_free(msg);
 }
 
 /* parse incoming */
@@ -519,55 +647,65 @@
 static void
 process_init(void)
 {
-	Buffer msg;
+	struct sshbuf *msg;
+	int r;
 
-	version = get_int();
+	if ((r = sshbuf_get_u32(iqueue, &version)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
 	verbose("received client version %u", version);
-	buffer_init(&msg);
-	buffer_put_char(&msg, SSH2_FXP_VERSION);
-	buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
-	/* POSIX rename extension */
-	buffer_put_cstring(&msg, "posix-rename@openssh.com");
-	buffer_put_cstring(&msg, "1"); /* version */
-	/* statvfs extension */
-	buffer_put_cstring(&msg, "statvfs@openssh.com");
-	buffer_put_cstring(&msg, "2"); /* version */
-	/* fstatvfs extension */
-	buffer_put_cstring(&msg, "fstatvfs@openssh.com");
-	buffer_put_cstring(&msg, "2"); /* version */
-	/* hardlink extension */
-	buffer_put_cstring(&msg, "hardlink@openssh.com");
-	buffer_put_cstring(&msg, "1"); /* version */
-	send_msg(&msg);
-	buffer_free(&msg);
+	if ((msg = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new failed", __func__);
+	if ((r = sshbuf_put_u8(msg, SSH2_FXP_VERSION)) != 0 ||
+	    (r = sshbuf_put_u32(msg, SSH2_FILEXFER_VERSION)) != 0 ||
+	    /* POSIX rename extension */
+	    (r = sshbuf_put_cstring(msg, "posix-rename@openssh.com")) != 0 ||
+	    (r = sshbuf_put_cstring(msg, "1")) != 0 || /* version */
+	    /* statvfs extension */
+	    (r = sshbuf_put_cstring(msg, "statvfs@openssh.com")) != 0 ||
+	    (r = sshbuf_put_cstring(msg, "2")) != 0 || /* version */
+	    /* fstatvfs extension */
+	    (r = sshbuf_put_cstring(msg, "fstatvfs@openssh.com")) != 0 ||
+	    (r = sshbuf_put_cstring(msg, "2")) != 0 || /* version */
+	    /* hardlink extension */
+	    (r = sshbuf_put_cstring(msg, "hardlink@openssh.com")) != 0 ||
+	    (r = sshbuf_put_cstring(msg, "1")) != 0 || /* version */
+	    /* fsync extension */
+	    (r = sshbuf_put_cstring(msg, "fsync@openssh.com")) != 0 ||
+	    (r = sshbuf_put_cstring(msg, "1")) != 0) /* version */
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+	send_msg(msg);
+	sshbuf_free(msg);
 }
 
 static void
-process_open(void)
+process_open(u_int32_t id)
 {
-	u_int32_t id, pflags;
-	Attrib *a;
+	u_int32_t pflags;
+	Attrib a;
 	char *name;
-	int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
+	int r, handle, fd, flags, mode, status = SSH2_FX_FAILURE;
 
-	id = get_int();
-	name = get_string(NULL);
-	pflags = get_int();		/* portable flags */
+	if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 ||
+	    (r = sshbuf_get_u32(iqueue, &pflags)) != 0 || /* portable flags */
+	    (r = decode_attrib(iqueue, &a)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
 	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;
+	mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a.perm : 0666;
 	logit("open \"%s\" flags %s mode 0%o",
 	    name, string_from_portable(pflags), mode);
 	if (readonly &&
-	    ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR))
+	    ((flags & O_ACCMODE) == O_WRONLY ||
+	    (flags & O_ACCMODE) == O_RDWR)) {
+		verbose("Refusing open request in read-only mode");
 		status = SSH2_FX_PERMISSION_DENIED;
-	else {
+	} else {
 		fd = open(name, flags, mode);
 		if (fd < 0) {
 			status = errno_to_portable(errno);
 		} else {
-			handle = handle_new(HANDLE_FILE, name, fd, NULL);
+			handle = handle_new(HANDLE_FILE, name, fd, flags, NULL);
 			if (handle < 0) {
 				close(fd);
 			} else {
@@ -578,17 +716,17 @@
 	}
 	if (status != SSH2_FX_OK)
 		send_status(id, status);
-	xfree(name);
+	free(name);
 }
 
 static void
-process_close(void)
+process_close(u_int32_t id)
 {
-	u_int32_t id;
-	int handle, ret, status = SSH2_FX_FAILURE;
+	int r, handle, ret, status = SSH2_FX_FAILURE;
 
-	id = get_int();
-	handle = get_handle();
+	if ((r = get_handle(iqueue, &handle)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
 	debug3("request %u: close handle %u", id, handle);
 	handle_log_close(handle, NULL);
 	ret = handle_close(handle);
@@ -597,17 +735,17 @@
 }
 
 static void
-process_read(void)
+process_read(u_int32_t id)
 {
-	char buf[64*1024];
-	u_int32_t id, len;
-	int handle, fd, ret, status = SSH2_FX_FAILURE;
+	u_char buf[64*1024];
+	u_int32_t len;
+	int r, handle, fd, ret, status = SSH2_FX_FAILURE;
 	u_int64_t off;
 
-	id = get_int();
-	handle = get_handle();
-	off = get_int64();
-	len = get_int();
+	if ((r = get_handle(iqueue, &handle)) != 0 ||
+	    (r = sshbuf_get_u64(iqueue, &off)) != 0 ||
+	    (r = sshbuf_get_u32(iqueue, &len)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
 
 	debug("request %u: read \"%s\" (handle %d) off %llu len %d",
 	    id, handle_to_name(handle), handle, (unsigned long long)off, len);
@@ -638,29 +776,27 @@
 }
 
 static void
-process_write(void)
+process_write(u_int32_t id)
 {
-	u_int32_t id;
 	u_int64_t off;
-	u_int len;
-	int handle, fd, ret, status;
-	char *data;
+	size_t len;
+	int r, handle, fd, ret, status;
+	u_char *data;
 
-	id = get_int();
-	handle = get_handle();
-	off = get_int64();
-	data = get_string(&len);
+	if ((r = get_handle(iqueue, &handle)) != 0 ||
+	    (r = sshbuf_get_u64(iqueue, &off)) != 0 ||
+	    (r = sshbuf_get_string(iqueue, &data, &len)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
 
-	debug("request %u: write \"%s\" (handle %d) off %llu len %d",
+	debug("request %u: write \"%s\" (handle %d) off %llu len %zu",
 	    id, handle_to_name(handle), handle, (unsigned long long)off, len);
 	fd = handle_to_fd(handle);
-	
+
 	if (fd < 0)
 		status = SSH2_FX_FAILURE;
-	else if (readonly)
-		status = SSH2_FX_PERMISSION_DENIED;
 	else {
-		if (lseek(fd, off, SEEK_SET) < 0) {
+		if (!(handle_to_flags(handle) & O_APPEND) &&
+				lseek(fd, off, SEEK_SET) < 0) {
 			status = errno_to_portable(errno);
 			error("process_write: seek failed");
 		} else {
@@ -679,24 +815,24 @@
 		}
 	}
 	send_status(id, status);
-	xfree(data);
+	free(data);
 }
 
 static void
-process_do_stat(int do_lstat)
+process_do_stat(u_int32_t id, int do_lstat)
 {
 	Attrib a;
 	struct stat st;
-	u_int32_t id;
 	char *name;
-	int ret, status = SSH2_FX_FAILURE;
+	int r, status = SSH2_FX_FAILURE;
 
-	id = get_int();
-	name = get_string(NULL);
+	if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
 	debug3("request %u: %sstat", id, do_lstat ? "l" : "");
 	verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name);
-	ret = do_lstat ? lstat(name, &st) : stat(name, &st);
-	if (ret < 0) {
+	r = do_lstat ? lstat(name, &st) : stat(name, &st);
+	if (r < 0) {
 		status = errno_to_portable(errno);
 	} else {
 		stat_to_attrib(&st, &a);
@@ -705,37 +841,36 @@
 	}
 	if (status != SSH2_FX_OK)
 		send_status(id, status);
-	xfree(name);
+	free(name);
 }
 
 static void
-process_stat(void)
+process_stat(u_int32_t id)
 {
-	process_do_stat(0);
+	process_do_stat(id, 0);
 }
 
 static void
-process_lstat(void)
+process_lstat(u_int32_t id)
 {
-	process_do_stat(1);
+	process_do_stat(id, 1);
 }
 
 static void
-process_fstat(void)
+process_fstat(u_int32_t id)
 {
 	Attrib a;
 	struct stat st;
-	u_int32_t id;
-	int fd, ret, handle, status = SSH2_FX_FAILURE;
+	int fd, r, handle, status = SSH2_FX_FAILURE;
 
-	id = get_int();
-	handle = get_handle();
+	if ((r = get_handle(iqueue, &handle)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
 	debug("request %u: fstat \"%s\" (handle %u)",
 	    id, handle_to_name(handle), handle);
 	fd = handle_to_fd(handle);
 	if (fd >= 0) {
-		ret = fstat(fd, &st);
-		if (ret < 0) {
+		r = fstat(fd, &st);
+		if (r < 0) {
 			status = errno_to_portable(errno);
 		} else {
 			stat_to_attrib(&st, &a);
@@ -760,117 +895,111 @@
 }
 
 static void
-process_setstat(void)
+process_setstat(u_int32_t id)
 {
-	Attrib *a;
-	u_int32_t id;
+	Attrib a;
 	char *name;
-	int status = SSH2_FX_OK, ret;
+	int r, status = SSH2_FX_OK;
 
-	id = get_int();
-	name = get_string(NULL);
-	a = get_attrib();
+	if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 ||
+	    (r = decode_attrib(iqueue, &a)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
 	debug("request %u: setstat name \"%s\"", id, name);
-	if (readonly) {
-		status = SSH2_FX_PERMISSION_DENIED;
-		a->flags = 0;
-	}
-	if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
+	if (a.flags & SSH2_FILEXFER_ATTR_SIZE) {
 		logit("set \"%s\" size %llu",
-		    name, (unsigned long long)a->size);
-		ret = truncate(name, a->size);
-		if (ret == -1)
+		    name, (unsigned long long)a.size);
+		r = truncate(name, a.size);
+		if (r == -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 & 07777);
-		if (ret == -1)
+	if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
+		logit("set \"%s\" mode %04o", name, a.perm);
+		r = chmod(name, a.perm & 07777);
+		if (r == -1)
 			status = errno_to_portable(errno);
 	}
-	if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
+	if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
 		char buf[64];
-		time_t t = a->mtime;
+		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)
+		r = utimes(name, attrib_to_tv(&a));
+		if (r == -1)
 			status = errno_to_portable(errno);
 	}
-	if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
+	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)
+		    (u_long)a.uid, (u_long)a.gid);
+		r = chown(name, a.uid, a.gid);
+		if (r == -1)
 			status = errno_to_portable(errno);
 	}
 	send_status(id, status);
-	xfree(name);
+	free(name);
 }
 
 static void
-process_fsetstat(void)
+process_fsetstat(u_int32_t id)
 {
-	Attrib *a;
-	u_int32_t id;
-	int handle, fd, ret;
+	Attrib a;
+	int handle, fd, r;
 	int status = SSH2_FX_OK;
 
-	id = get_int();
-	handle = get_handle();
-	a = get_attrib();
+	if ((r = get_handle(iqueue, &handle)) != 0 ||
+	    (r = decode_attrib(iqueue, &a)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
 	debug("request %u: fsetstat handle %d", id, handle);
 	fd = handle_to_fd(handle);
 	if (fd < 0)
 		status = SSH2_FX_FAILURE;
-	else if (readonly)
-		status = SSH2_FX_PERMISSION_DENIED;
 	else {
 		char *name = handle_to_name(handle);
 
-		if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
+		if (a.flags & SSH2_FILEXFER_ATTR_SIZE) {
 			logit("set \"%s\" size %llu",
-			    name, (unsigned long long)a->size);
-			ret = ftruncate(fd, a->size);
-			if (ret == -1)
+			    name, (unsigned long long)a.size);
+			r = ftruncate(fd, a.size);
+			if (r == -1)
 				status = errno_to_portable(errno);
 		}
-		if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
-			logit("set \"%s\" mode %04o", name, a->perm);
+		if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
+			logit("set \"%s\" mode %04o", name, a.perm);
 #ifdef HAVE_FCHMOD
-			ret = fchmod(fd, a->perm & 07777);
+			r = fchmod(fd, a.perm & 07777);
 #else
-			ret = chmod(name, a->perm & 07777);
+			r = chmod(name, a.perm & 07777);
 #endif
-			if (ret == -1)
+			if (r == -1)
 				status = errno_to_portable(errno);
 		}
-		if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
+		if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
 			char buf[64];
-			time_t t = a->mtime;
+			time_t t = a.mtime;
 
 			strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S",
 			    localtime(&t));
 			logit("set \"%s\" modtime %s", name, buf);
 #ifdef HAVE_FUTIMES
-			ret = futimes(fd, attrib_to_tv(a));
+			r = futimes(fd, attrib_to_tv(&a));
 #else
-			ret = utimes(name, attrib_to_tv(a));
+			r = utimes(name, attrib_to_tv(&a));
 #endif
-			if (ret == -1)
+			if (r == -1)
 				status = errno_to_portable(errno);
 		}
-		if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
+		if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) {
 			logit("set \"%s\" owner %lu group %lu", name,
-			    (u_long)a->uid, (u_long)a->gid);
+			    (u_long)a.uid, (u_long)a.gid);
 #ifdef HAVE_FCHOWN
-			ret = fchown(fd, a->uid, a->gid);
+			r = fchown(fd, a.uid, a.gid);
 #else
-			ret = chown(name, a->uid, a->gid);
+			r = chown(name, a.uid, a.gid);
 #endif
-			if (ret == -1)
+			if (r == -1)
 				status = errno_to_portable(errno);
 		}
 	}
@@ -878,22 +1007,22 @@
 }
 
 static void
-process_opendir(void)
+process_opendir(u_int32_t id)
 {
 	DIR *dirp = NULL;
 	char *path;
-	int handle, status = SSH2_FX_FAILURE;
-	u_int32_t id;
+	int r, handle, status = SSH2_FX_FAILURE;
 
-	id = get_int();
-	path = get_string(NULL);
+	if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
 	debug3("request %u: opendir", id);
 	logit("opendir \"%s\"", path);
 	dirp = opendir(path);
 	if (dirp == NULL) {
 		status = errno_to_portable(errno);
 	} else {
-		handle = handle_new(HANDLE_DIR, path, 0, dirp);
+		handle = handle_new(HANDLE_DIR, path, 0, 0, dirp);
 		if (handle < 0) {
 			closedir(dirp);
 		} else {
@@ -904,20 +1033,20 @@
 	}
 	if (status != SSH2_FX_OK)
 		send_status(id, status);
-	xfree(path);
+	free(path);
 }
 
 static void
-process_readdir(void)
+process_readdir(u_int32_t id)
 {
 	DIR *dirp;
 	struct dirent *dp;
 	char *path;
-	int handle;
-	u_int32_t id;
+	int r, handle;
 
-	id = get_int();
-	handle = get_handle();
+	if ((r = get_handle(iqueue, &handle)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
 	debug("request %u: readdir \"%s\" (handle %d)", id,
 	    handle_to_name(handle), handle);
 	dirp = handle_to_dir(handle);
@@ -926,7 +1055,7 @@
 		send_status(id, SSH2_FX_FAILURE);
 	} else {
 		struct stat st;
-		char pathname[MAXPATHLEN];
+		char pathname[PATH_MAX];
 		Stat *stats;
 		int nstats = 10, count = 0, i;
 
@@ -953,95 +1082,83 @@
 		if (count > 0) {
 			send_names(id, count, stats);
 			for (i = 0; i < count; i++) {
-				xfree(stats[i].name);
-				xfree(stats[i].long_name);
+				free(stats[i].name);
+				free(stats[i].long_name);
 			}
 		} else {
 			send_status(id, SSH2_FX_EOF);
 		}
-		xfree(stats);
+		free(stats);
 	}
 }
 
 static void
-process_remove(void)
+process_remove(u_int32_t id)
 {
 	char *name;
-	u_int32_t id;
-	int status = SSH2_FX_FAILURE;
-	int ret;
+	int r, status = SSH2_FX_FAILURE;
 
-	id = get_int();
-	name = get_string(NULL);
+	if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
 	debug3("request %u: remove", id);
 	logit("remove name \"%s\"", name);
-	if (readonly)
-		status = SSH2_FX_PERMISSION_DENIED;
-	else {
-		ret = unlink(name);
-		status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
-	}
+	r = unlink(name);
+	status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
 	send_status(id, status);
-	xfree(name);
+	free(name);
 }
 
 static void
-process_mkdir(void)
+process_mkdir(u_int32_t id)
 {
-	Attrib *a;
-	u_int32_t id;
+	Attrib a;
 	char *name;
-	int ret, mode, status = SSH2_FX_FAILURE;
+	int r, mode, status = SSH2_FX_FAILURE;
 
-	id = get_int();
-	name = get_string(NULL);
-	a = get_attrib();
-	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
-	    a->perm & 07777 : 0777;
+	if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 ||
+	    (r = decode_attrib(iqueue, &a)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
+	mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
+	    a.perm & 07777 : 0777;
 	debug3("request %u: mkdir", id);
 	logit("mkdir name \"%s\" mode 0%o", name, mode);
-	if (readonly)
-		status = SSH2_FX_PERMISSION_DENIED;
-	else {
-		ret = mkdir(name, mode);
-		status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
-	}
+	r = mkdir(name, mode);
+	status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
 	send_status(id, status);
-	xfree(name);
+	free(name);
 }
 
 static void
-process_rmdir(void)
+process_rmdir(u_int32_t id)
 {
-	u_int32_t id;
 	char *name;
-	int ret, status;
+	int r, status;
 
-	id = get_int();
-	name = get_string(NULL);
+	if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
 	debug3("request %u: rmdir", id);
 	logit("rmdir name \"%s\"", name);
-	if (readonly)
-		status = SSH2_FX_PERMISSION_DENIED;
-	else {
-		ret = rmdir(name);
-		status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
-	}
+	r = rmdir(name);
+	status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
 	send_status(id, status);
-	xfree(name);
+	free(name);
 }
 
 static void
-process_realpath(void)
+process_realpath(u_int32_t id)
 {
-	char resolvedname[MAXPATHLEN];
-	u_int32_t id;
+	char resolvedname[PATH_MAX];
 	char *path;
+	int r;
 
-	id = get_int();
-	path = get_string(NULL);
+	if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
 	if (path[0] == '\0') {
-		xfree(path);
+		free(path);
 		path = xstrdup(".");
 	}
 	debug3("request %u: realpath", id);
@@ -1054,26 +1171,24 @@
 		s.name = s.long_name = resolvedname;
 		send_names(id, 1, &s);
 	}
-	xfree(path);
+	free(path);
 }
 
 static void
-process_rename(void)
+process_rename(u_int32_t id)
 {
-	u_int32_t id;
 	char *oldpath, *newpath;
-	int status;
+	int r, status;
 	struct stat sb;
 
-	id = get_int();
-	oldpath = get_string(NULL);
-	newpath = get_string(NULL);
+	if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 ||
+	    (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
 	debug3("request %u: rename", id);
 	logit("rename old \"%s\" new \"%s\"", oldpath, newpath);
 	status = SSH2_FX_FAILURE;
-	if (readonly)
-		status = SSH2_FX_PERMISSION_DENIED;
-	else if (lstat(oldpath, &sb) == -1)
+	if (lstat(oldpath, &sb) == -1)
 		status = errno_to_portable(errno);
 	else if (S_ISREG(sb.st_mode)) {
 		/* Race-free rename of regular files */
@@ -1115,20 +1230,20 @@
 			status = SSH2_FX_OK;
 	}
 	send_status(id, status);
-	xfree(oldpath);
-	xfree(newpath);
+	free(oldpath);
+	free(newpath);
 }
 
 static void
-process_readlink(void)
+process_readlink(u_int32_t id)
 {
-	u_int32_t id;
-	int len;
-	char buf[MAXPATHLEN];
+	int r, len;
+	char buf[PATH_MAX];
 	char *path;
 
-	id = get_int();
-	path = get_string(NULL);
+	if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
 	debug3("request %u: readlink", id);
 	verbose("readlink \"%s\"", path);
 	if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1)
@@ -1141,52 +1256,46 @@
 		s.name = s.long_name = buf;
 		send_names(id, 1, &s);
 	}
-	xfree(path);
+	free(path);
 }
 
 static void
-process_symlink(void)
+process_symlink(u_int32_t id)
 {
-	u_int32_t id;
 	char *oldpath, *newpath;
-	int ret, status;
+	int r, status;
 
-	id = get_int();
-	oldpath = get_string(NULL);
-	newpath = get_string(NULL);
+	if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 ||
+	    (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
 	debug3("request %u: symlink", id);
 	logit("symlink old \"%s\" new \"%s\"", oldpath, newpath);
 	/* this will fail if 'newpath' exists */
-	if (readonly)
-		status = SSH2_FX_PERMISSION_DENIED;
-	else {
-		ret = symlink(oldpath, newpath);
-		status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
-	}
+	r = symlink(oldpath, newpath);
+	status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
 	send_status(id, status);
-	xfree(oldpath);
-	xfree(newpath);
+	free(oldpath);
+	free(newpath);
 }
 
 static void
 process_extended_posix_rename(u_int32_t id)
 {
 	char *oldpath, *newpath;
-	int ret, status;
+	int r, status;
 
-	oldpath = get_string(NULL);
-	newpath = get_string(NULL);
+	if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 ||
+	    (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
 	debug3("request %u: posix-rename", id);
 	logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath);
-	if (readonly)
-		status = SSH2_FX_PERMISSION_DENIED;
-	else {
-		ret = rename(oldpath, newpath);
-		status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
-	}
+	r = rename(oldpath, newpath);
+	status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
 	send_status(id, status);
-	xfree(oldpath);
-	xfree(newpath);
+	free(oldpath);
+	free(newpath);
 }
 
 static void
@@ -1194,25 +1303,28 @@
 {
 	char *path;
 	struct statvfs st;
+	int r;
 
-	path = get_string(NULL);
-	debug3("request %u: statfs", id);
-	logit("statfs \"%s\"", path);
+	if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+	debug3("request %u: statvfs", id);
+	logit("statvfs \"%s\"", path);
 
 	if (statvfs(path, &st) != 0)
 		send_status(id, errno_to_portable(errno));
 	else
 		send_statvfs(id, &st);
-        xfree(path);
+        free(path);
 }
 
 static void
 process_extended_fstatvfs(u_int32_t id)
 {
-	int handle, fd;
+	int r, handle, fd;
 	struct statvfs st;
 
-	handle = get_handle();
+	if ((r = get_handle(iqueue, &handle)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
 	debug("request %u: fstatvfs \"%s\" (handle %u)",
 	    id, handle_to_name(handle), handle);
 	if ((fd = handle_to_fd(handle)) < 0) {
@@ -1229,42 +1341,61 @@
 process_extended_hardlink(u_int32_t id)
 {
 	char *oldpath, *newpath;
-	int ret, status;
+	int r, status;
 
-	oldpath = get_string(NULL);
-	newpath = get_string(NULL);
+	if ((r = sshbuf_get_cstring(iqueue, &oldpath, NULL)) != 0 ||
+	    (r = sshbuf_get_cstring(iqueue, &newpath, NULL)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
 	debug3("request %u: hardlink", id);
 	logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath);
-	if (readonly)
-		status = SSH2_FX_PERMISSION_DENIED;
-	else {
-		ret = link(oldpath, newpath);
-		status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
-	}
+	r = link(oldpath, newpath);
+	status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
 	send_status(id, status);
-	xfree(oldpath);
-	xfree(newpath);
+	free(oldpath);
+	free(newpath);
 }
 
 static void
-process_extended(void)
+process_extended_fsync(u_int32_t id)
 {
-	u_int32_t id;
-	char *request;
+	int handle, fd, r, status = SSH2_FX_OP_UNSUPPORTED;
 
-	id = get_int();
-	request = get_string(NULL);
-	if (strcmp(request, "posix-rename@openssh.com") == 0)
-		process_extended_posix_rename(id);
-	else if (strcmp(request, "statvfs@openssh.com") == 0)
-		process_extended_statvfs(id);
-	else if (strcmp(request, "fstatvfs@openssh.com") == 0)
-		process_extended_fstatvfs(id);
-	else if (strcmp(request, "hardlink@openssh.com") == 0)
-		process_extended_hardlink(id);
-	else
+	if ((r = get_handle(iqueue, &handle)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+	debug3("request %u: fsync (handle %u)", id, handle);
+	verbose("fsync \"%s\"", handle_to_name(handle));
+	if ((fd = handle_to_fd(handle)) < 0)
+		status = SSH2_FX_NO_SUCH_FILE;
+	else if (handle_is_ok(handle, HANDLE_FILE)) {
+		r = fsync(fd);
+		status = (r == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
+	}
+	send_status(id, status);
+}
+
+static void
+process_extended(u_int32_t id)
+{
+	char *request;
+	int i, r;
+
+	if ((r = sshbuf_get_cstring(iqueue, &request, NULL)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+	for (i = 0; extended_handlers[i].handler != NULL; i++) {
+		if (strcmp(request, extended_handlers[i].ext_name) == 0) {
+			if (!request_permitted(&extended_handlers[i]))
+				send_status(id, SSH2_FX_PERMISSION_DENIED);
+			else
+				extended_handlers[i].handler(id);
+			break;
+		}
+	}
+	if (extended_handlers[i].handler == NULL) {
+		error("Unknown extended request \"%.100s\"", request);
 		send_status(id, SSH2_FX_OP_UNSUPPORTED);	/* MUST */
-	xfree(request);
+	}
+	free(request);
 }
 
 /* stolen from ssh-agent */
@@ -1275,13 +1406,15 @@
 	u_int msg_len;
 	u_int buf_len;
 	u_int consumed;
-	u_int type;
-	u_char *cp;
+	u_char type;
+	const u_char *cp;
+	int i, r;
+	u_int32_t id;
 
-	buf_len = buffer_len(&iqueue);
+	buf_len = sshbuf_len(iqueue);
 	if (buf_len < 5)
 		return;		/* Incomplete message. */
-	cp = buffer_ptr(&iqueue);
+	cp = sshbuf_ptr(iqueue);
 	msg_len = get_u32(cp);
 	if (msg_len > SFTP_MAX_MSG_LENGTH) {
 		error("bad message from %s local user %s",
@@ -1290,86 +1423,56 @@
 	}
 	if (buf_len < msg_len + 4)
 		return;
-	buffer_consume(&iqueue, 4);
+	if ((r = sshbuf_consume(iqueue, 4)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
 	buf_len -= 4;
-	type = buffer_get_char(&iqueue);
+	if ((r = sshbuf_get_u8(iqueue, &type)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
+
 	switch (type) {
 	case SSH2_FXP_INIT:
 		process_init();
-		break;
-	case SSH2_FXP_OPEN:
-		process_open();
-		break;
-	case SSH2_FXP_CLOSE:
-		process_close();
-		break;
-	case SSH2_FXP_READ:
-		process_read();
-		break;
-	case SSH2_FXP_WRITE:
-		process_write();
-		break;
-	case SSH2_FXP_LSTAT:
-		process_lstat();
-		break;
-	case SSH2_FXP_FSTAT:
-		process_fstat();
-		break;
-	case SSH2_FXP_SETSTAT:
-		process_setstat();
-		break;
-	case SSH2_FXP_FSETSTAT:
-		process_fsetstat();
-		break;
-	case SSH2_FXP_OPENDIR:
-		process_opendir();
-		break;
-	case SSH2_FXP_READDIR:
-		process_readdir();
-		break;
-	case SSH2_FXP_REMOVE:
-		process_remove();
-		break;
-	case SSH2_FXP_MKDIR:
-		process_mkdir();
-		break;
-	case SSH2_FXP_RMDIR:
-		process_rmdir();
-		break;
-	case SSH2_FXP_REALPATH:
-		process_realpath();
-		break;
-	case SSH2_FXP_STAT:
-		process_stat();
-		break;
-	case SSH2_FXP_RENAME:
-		process_rename();
-		break;
-	case SSH2_FXP_READLINK:
-		process_readlink();
-		break;
-	case SSH2_FXP_SYMLINK:
-		process_symlink();
+		init_done = 1;
 		break;
 	case SSH2_FXP_EXTENDED:
-		process_extended();
+		if (!init_done)
+			fatal("Received extended request before init");
+		if ((r = sshbuf_get_u32(iqueue, &id)) != 0)
+			fatal("%s: buffer error: %s", __func__, ssh_err(r));
+		process_extended(id);
 		break;
 	default:
-		error("Unknown message %d", type);
-		break;
+		if (!init_done)
+			fatal("Received %u request before init", type);
+		if ((r = sshbuf_get_u32(iqueue, &id)) != 0)
+			fatal("%s: buffer error: %s", __func__, ssh_err(r));
+		for (i = 0; handlers[i].handler != NULL; i++) {
+			if (type == handlers[i].type) {
+				if (!request_permitted(&handlers[i])) {
+					send_status(id,
+					    SSH2_FX_PERMISSION_DENIED);
+				} else {
+					handlers[i].handler(id);
+				}
+				break;
+			}
+		}
+		if (handlers[i].handler == NULL)
+			error("Unknown message %u", type);
 	}
 	/* discard the remaining bytes from the current packet */
-	if (buf_len < buffer_len(&iqueue)) {
+	if (buf_len < sshbuf_len(iqueue)) {
 		error("iqueue grew unexpectedly");
 		sftp_server_cleanup_exit(255);
 	}
-	consumed = buf_len - buffer_len(&iqueue);
+	consumed = buf_len - sshbuf_len(iqueue);
 	if (msg_len < consumed) {
-		error("msg_len %d < consumed %d", msg_len, consumed);
+		error("msg_len %u < consumed %u", msg_len, consumed);
 		sftp_server_cleanup_exit(255);
 	}
-	if (msg_len > consumed)
-		buffer_consume(&iqueue, msg_len - consumed);
+	if (msg_len > consumed &&
+	    (r = sshbuf_consume(iqueue, msg_len - consumed)) != 0)
+		fatal("%s: buffer error: %s", __func__, ssh_err(r));
 }
 
 /* Cleanup handler that logs active handles upon normal exit */
@@ -1390,8 +1493,11 @@
 	extern char *__progname;
 
 	fprintf(stderr,
-	    "usage: %s [-ehR] [-f log_facility] [-l log_level] [-u umask]\n",
-	    __progname);
+	    "usage: %s [-ehR] [-d start_directory] [-f log_facility] "
+	    "[-l log_level]\n\t[-P blacklisted_requests] "
+	    "[-p whitelisted_requests] [-u umask]\n"
+	    "       %s -Q protocol_feature\n",
+	    __progname, __progname);
 	exit(1);
 }
 
@@ -1399,10 +1505,10 @@
 sftp_server_main(int argc, char **argv, struct passwd *user_pw)
 {
 	fd_set *rset, *wset;
-	int in, out, max, ch, skipargs = 0, log_stderr = 0;
+	int i, r, in, out, max, ch, skipargs = 0, log_stderr = 0;
 	ssize_t len, olen, set_size;
 	SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
-	char *cp, buf[4*4096];
+	char *cp, *homedir = NULL, buf[4*4096];
 	long mask;
 
 	extern char *optarg;
@@ -1411,8 +1517,22 @@
 	__progname = ssh_get_progname(argv[0]);
 	log_init(__progname, log_level, log_facility, log_stderr);
 
-	while (!skipargs && (ch = getopt(argc, argv, "f:l:u:cehR")) != -1) {
+	pw = pwcopy(user_pw);
+
+	while (!skipargs && (ch = getopt(argc, argv,
+	    "d:f:l:P:p:Q:u:cehR")) != -1) {
 		switch (ch) {
+		case 'Q':
+			if (strcasecmp(optarg, "requests") != 0) {
+				fprintf(stderr, "Invalid query type\n");
+				exit(1);
+			}
+			for (i = 0; handlers[i].handler != NULL; i++)
+				printf("%s\n", handlers[i].name);
+			for (i = 0; extended_handlers[i].handler != NULL; i++)
+				printf("%s\n", extended_handlers[i].name);
+			exit(0);
+			break;
 		case 'R':
 			readonly = 1;
 			break;
@@ -1436,6 +1556,22 @@
 			if (log_facility == SYSLOG_FACILITY_NOT_SET)
 				error("Invalid log facility \"%s\"", optarg);
 			break;
+		case 'd':
+			cp = tilde_expand_filename(optarg, user_pw->pw_uid);
+			homedir = percent_expand(cp, "d", user_pw->pw_dir,
+			    "u", user_pw->pw_name, (char *)NULL);
+			free(cp);
+			break;
+		case 'p':
+			if (request_whitelist != NULL)
+				fatal("Permitted requests already set");
+			request_whitelist = xstrdup(optarg);
+			break;
+		case 'P':
+			if (request_blacklist != NULL)
+				fatal("Refused requests already set");
+			request_blacklist = xstrdup(optarg);
+			break;
 		case 'u':
 			errno = 0;
 			mask = strtol(optarg, &cp, 8);
@@ -1452,6 +1588,17 @@
 
 	log_init(__progname, log_level, log_facility, log_stderr);
 
+#if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
+	/*
+	 * On Linux, we should try to avoid making /proc/self/{mem,maps}
+	 * available to the user so that sftp access doesn't automatically
+	 * imply arbitrary code execution access that will break
+	 * restricted configurations.
+	 */
+	if (prctl(PR_SET_DUMPABLE, 0) != 0)
+		fatal("unable to make the process undumpable");
+#endif /* defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE) */
+
 	if ((cp = getenv("SSH_CONNECTION")) != NULL) {
 		client_addr = xstrdup(cp);
 		if ((cp = strchr(client_addr, ' ')) == NULL) {
@@ -1463,8 +1610,6 @@
 	} else
 		client_addr = xstrdup("UNKNOWN");
 
-	pw = pwcopy(user_pw);
-
 	logit("session opened for local user %s from [%s]",
 	    pw->pw_name, client_addr);
 
@@ -1482,13 +1627,22 @@
 	if (out > max)
 		max = out;
 
-	buffer_init(&iqueue);
-	buffer_init(&oqueue);
+	if ((iqueue = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new failed", __func__);
+	if ((oqueue = sshbuf_new()) == NULL)
+		fatal("%s: sshbuf_new failed", __func__);
 
 	set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
 	rset = (fd_set *)xmalloc(set_size);
 	wset = (fd_set *)xmalloc(set_size);
 
+	if (homedir != NULL) {
+		if (chdir(homedir) != 0) {
+			error("chdir to \"%s\" failed: %s", homedir,
+			    strerror(errno));
+		}
+	}
+
 	for (;;) {
 		memset(rset, 0, set_size);
 		memset(wset, 0, set_size);
@@ -1498,11 +1652,15 @@
 		 * the worst-case length packet it can generate,
 		 * otherwise apply backpressure by stopping reads.
 		 */
-		if (buffer_check_alloc(&iqueue, sizeof(buf)) &&
-		    buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH))
+		if ((r = sshbuf_check_reserve(iqueue, sizeof(buf))) == 0 &&
+		    (r = sshbuf_check_reserve(oqueue,
+		    SFTP_MAX_MSG_LENGTH)) == 0)
 			FD_SET(in, rset);
+		else if (r != SSH_ERR_NO_BUFFER_SPACE)
+			fatal("%s: sshbuf_check_reserve failed: %s",
+			    __func__, ssh_err(r));
 
-		olen = buffer_len(&oqueue);
+		olen = sshbuf_len(oqueue);
 		if (olen > 0)
 			FD_SET(out, wset);
 
@@ -1522,18 +1680,20 @@
 			} else if (len < 0) {
 				error("read: %s", strerror(errno));
 				sftp_server_cleanup_exit(1);
-			} else {
-				buffer_append(&iqueue, buf, len);
+			} else if ((r = sshbuf_put(iqueue, buf, len)) != 0) {
+				fatal("%s: buffer error: %s",
+				    __func__, ssh_err(r));
 			}
 		}
 		/* send oqueue to stdout */
 		if (FD_ISSET(out, wset)) {
-			len = write(out, buffer_ptr(&oqueue), olen);
+			len = write(out, sshbuf_ptr(oqueue), olen);
 			if (len < 0) {
 				error("write: %s", strerror(errno));
 				sftp_server_cleanup_exit(1);
-			} else {
-				buffer_consume(&oqueue, len);
+			} else if ((r = sshbuf_consume(oqueue, len)) != 0) {
+				fatal("%s: buffer error: %s",
+				    __func__, ssh_err(r));
 			}
 		}
 
@@ -1542,7 +1702,11 @@
 		 * into the output buffer, otherwise stop processing input
 		 * and let the output queue drain.
 		 */
-		if (buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH))
+		r = sshbuf_check_reserve(oqueue, SFTP_MAX_MSG_LENGTH);
+		if (r == 0)
 			process();
+		else if (r != SSH_ERR_NO_BUFFER_SPACE)
+			fatal("%s: sshbuf_check_reserve: %s",
+			    __func__, ssh_err(r));
 	}
 }