diff options
Diffstat (limited to 'sftp-server.c')
-rw-r--r-- | sftp-server.c | 399 |
1 files changed, 267 insertions, 132 deletions
diff --git a/sftp-server.c b/sftp-server.c index 359204fa7..18d194911 100644 --- a/sftp-server.c +++ b/sftp-server.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sftp-server.c,v 1.117 2019/07/05 04:55:40 djm Exp $ */ +/* $OpenBSD: sftp-server.c,v 1.129 2021/08/09 23:47:44 djm Exp $ */ /* * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. * @@ -19,6 +19,7 @@ #include <sys/types.h> #include <sys/stat.h> +#include <sys/resource.h> #ifdef HAVE_SYS_TIME_H # include <sys/time.h> #endif @@ -53,6 +54,9 @@ char *sftp_realpath(const char *, char *); /* sftp-realpath.c */ +/* Maximum data read that we are willing to accept */ +#define SFTP_MAX_READ_LENGTH (SFTP_MAX_MSG_LENGTH - 1024) + /* Our verbosity */ static LogLevel log_level = SYSLOG_LEVEL_ERROR; @@ -74,7 +78,7 @@ static int init_done; static int readonly; /* Requests that are allowed/denied */ -static char *request_whitelist, *request_blacklist; +static char *request_allowlist, *request_denylist; /* portable attributes, etc. */ typedef struct Stat Stat; @@ -110,6 +114,8 @@ 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_lsetstat(u_int32_t id); +static void process_extended_limits(u_int32_t id); +static void process_extended_expand(u_int32_t id); static void process_extended(u_int32_t id); struct sftp_handler { @@ -146,15 +152,30 @@ static const struct sftp_handler handlers[] = { /* SSH2_FXP_EXTENDED submessages */ static const struct sftp_handler extended_handlers[] = { { "posix-rename", "posix-rename@openssh.com", 0, - process_extended_posix_rename, 1 }, + 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 }, { "lsetstat", "lsetstat@openssh.com", 0, process_extended_lsetstat, 1 }, + { "limits", "limits@openssh.com", 0, process_extended_limits, 0 }, + { "expand-path", "expand-path@openssh.com", 0, + process_extended_expand, 0 }, { NULL, NULL, 0, NULL, 0 } }; +static const struct sftp_handler * +extended_handler_byname(const char *name) +{ + int i; + + for (i = 0; extended_handlers[i].handler != NULL; i++) { + if (strcmp(name, extended_handlers[i].ext_name) == 0) + return &extended_handlers[i]; + } + return NULL; +} + static int request_permitted(const struct sftp_handler *h) { @@ -164,20 +185,20 @@ request_permitted(const struct sftp_handler *h) 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) { + if (request_denylist != NULL && + ((result = match_list(h->name, request_denylist, NULL))) != NULL) { free(result); - verbose("Refusing blacklisted %s request", h->name); + verbose("Refusing denylisted %s request", h->name); return 0; } - if (request_whitelist != NULL && - ((result = match_list(h->name, request_whitelist, NULL))) != NULL) { + if (request_allowlist != NULL && + ((result = match_list(h->name, request_allowlist, NULL))) != NULL) { free(result); - debug2("Permitting whitelisted %s request", h->name); + debug2("Permitting allowlisted %s request", h->name); return 1; } - if (request_whitelist != NULL) { - verbose("Refusing non-whitelisted %s request", h->name); + if (request_allowlist != NULL) { + verbose("Refusing non-allowlisted %s request", h->name); return 0; } return 1; @@ -489,7 +510,7 @@ send_msg(struct sshbuf *m) int r; if ((r = sshbuf_put_stringb(oqueue, m)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "enqueue"); sshbuf_reset(m); } @@ -522,16 +543,16 @@ send_status(u_int32_t id, u_int32_t status) (status != SSH2_FX_OK && status != SSH2_FX_EOF)) logit("sent status %s", status_to_message(status)); if ((msg = sshbuf_new()) == NULL) - fatal("%s: sshbuf_new failed", __func__); + fatal_f("sshbuf_new failed"); 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)); + fatal_fr(r, "compose"); if (version >= 3) { 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)); + fatal_fr(r, "compose message"); } send_msg(msg); sshbuf_free(msg); @@ -543,11 +564,11 @@ send_data_or_handle(char type, u_int32_t id, const u_char *data, int dlen) int r; if ((msg = sshbuf_new()) == NULL) - fatal("%s: sshbuf_new failed", __func__); + fatal_f("sshbuf_new failed"); 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)); + fatal_fr(r, "compose"); send_msg(msg); sshbuf_free(msg); } @@ -578,17 +599,17 @@ send_names(u_int32_t id, int count, const Stat *stats) int i, r; if ((msg = sshbuf_new()) == NULL) - fatal("%s: sshbuf_new failed", __func__); + fatal_f("sshbuf_new failed"); 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)); + fatal_fr(r, "compose"); debug("request %u: sent names count %d", id, count); for (i = 0; i < count; i++) { 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)); + fatal_fr(r, "compose filenames/attrib"); } send_msg(msg); sshbuf_free(msg); @@ -602,11 +623,11 @@ send_attrib(u_int32_t id, const Attrib *a) debug("request %u: sent attrib have 0x%x", id, a->flags); if ((msg = sshbuf_new()) == NULL) - fatal("%s: sshbuf_new failed", __func__); + fatal_f("sshbuf_new failed"); 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)); + fatal_fr(r, "compose"); send_msg(msg); sshbuf_free(msg); } @@ -622,7 +643,7 @@ send_statvfs(u_int32_t id, struct statvfs *st) flag |= (st->f_flag & ST_NOSUID) ? SSH2_FXE_STATVFS_ST_NOSUID : 0; if ((msg = sshbuf_new()) == NULL) - fatal("%s: sshbuf_new failed", __func__); + fatal_f("sshbuf_new failed"); 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 || @@ -636,11 +657,33 @@ send_statvfs(u_int32_t id, struct statvfs *st) (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)); + fatal_fr(r, "compose"); send_msg(msg); sshbuf_free(msg); } +/* + * Prepare SSH2_FXP_VERSION extension advertisement for a single extension. + * The extension is checked for permission prior to advertisment. + */ +static int +compose_extension(struct sshbuf *msg, const char *name, const char *ver) +{ + int r; + const struct sftp_handler *exthnd; + + if ((exthnd = extended_handler_byname(name)) == NULL) + fatal_f("internal error: no handler for %s", name); + if (!request_permitted(exthnd)) { + debug2_f("refusing to advertise disallowed extension %s", name); + return 0; + } + if ((r = sshbuf_put_cstring(msg, name)) != 0 || + (r = sshbuf_put_cstring(msg, ver)) != 0) + fatal_fr(r, "compose %s", name); + return 0; +} + /* parse incoming */ static void @@ -650,30 +693,24 @@ process_init(void) int r; if ((r = sshbuf_get_u32(iqueue, &version)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "parse"); verbose("received client version %u", version); if ((msg = sshbuf_new()) == NULL) - fatal("%s: sshbuf_new failed", __func__); + fatal_f("sshbuf_new failed"); 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 */ - (r = sshbuf_put_cstring(msg, "lsetstat@openssh.com")) != 0 || - (r = sshbuf_put_cstring(msg, "1")) != 0) /* version */ - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + (r = sshbuf_put_u32(msg, SSH2_FILEXFER_VERSION)) != 0) + fatal_fr(r, "compose"); + + /* extension advertisments */ + compose_extension(msg, "posix-rename@openssh.com", "1"); + compose_extension(msg, "statvfs@openssh.com", "2"); + compose_extension(msg, "fstatvfs@openssh.com", "2"); + compose_extension(msg, "hardlink@openssh.com", "1"); + compose_extension(msg, "fsync@openssh.com", "1"); + compose_extension(msg, "lsetstat@openssh.com", "1"); + compose_extension(msg, "limits@openssh.com", "1"); + compose_extension(msg, "expand-path@openssh.com", "1"); + send_msg(msg); sshbuf_free(msg); } @@ -689,7 +726,7 @@ process_open(u_int32_t id) 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)); + fatal_fr(r, "parse"); debug3("request %u: open flags %d", id, pflags); flags = flags_from_portable(pflags); @@ -726,7 +763,7 @@ process_close(u_int32_t id) int r, handle, ret, status = SSH2_FX_FAILURE; if ((r = get_handle(iqueue, &handle)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "parse"); debug3("request %u: close handle %u", id, handle); handle_log_close(handle, NULL); @@ -738,7 +775,8 @@ process_close(u_int32_t id) static void process_read(u_int32_t id) { - u_char buf[64*1024]; + static u_char *buf; + static size_t buflen; u_int32_t len; int r, handle, fd, ret, status = SSH2_FX_FAILURE; u_int64_t off; @@ -746,32 +784,45 @@ process_read(u_int32_t id) 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)); + fatal_fr(r, "parse"); - debug("request %u: read \"%s\" (handle %d) off %llu len %d", + debug("request %u: read \"%s\" (handle %d) off %llu len %u", id, handle_to_name(handle), handle, (unsigned long long)off, len); - if (len > sizeof buf) { - len = sizeof buf; - debug2("read change len %d", len); + if ((fd = handle_to_fd(handle)) == -1) + goto out; + if (len > SFTP_MAX_READ_LENGTH) { + debug2("read change len %u to %u", len, SFTP_MAX_READ_LENGTH); + len = SFTP_MAX_READ_LENGTH; } - fd = handle_to_fd(handle); - if (fd >= 0) { - if (lseek(fd, off, SEEK_SET) == -1) { - error("process_read: seek failed"); - status = errno_to_portable(errno); - } else { - ret = read(fd, buf, len); - if (ret == -1) { - status = errno_to_portable(errno); - } else if (ret == 0) { - status = SSH2_FX_EOF; - } else { - send_data(id, buf, ret); - status = SSH2_FX_OK; - handle_update_read(handle, ret); - } - } + if (len > buflen) { + debug3_f("allocate %zu => %u", buflen, len); + if ((buf = realloc(NULL, len)) == NULL) + fatal_f("realloc failed"); + buflen = len; + } + if (lseek(fd, off, SEEK_SET) == -1) { + status = errno_to_portable(errno); + error_f("seek \"%.100s\": %s", handle_to_name(handle), + strerror(errno)); + goto out; } + if (len == 0) { + /* weird, but not strictly disallowed */ + ret = 0; + } else if ((ret = read(fd, buf, len)) == -1) { + status = errno_to_portable(errno); + error_f("read \"%.100s\": %s", handle_to_name(handle), + strerror(errno)); + goto out; + } else if (ret == 0) { + status = SSH2_FX_EOF; + goto out; + } + send_data(id, buf, ret); + handle_update_read(handle, ret); + /* success */ + status = SSH2_FX_OK; + out: if (status != SSH2_FX_OK) send_status(id, status); } @@ -787,7 +838,7 @@ process_write(u_int32_t id) 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)); + fatal_fr(r, "parse"); debug("request %u: write \"%s\" (handle %d) off %llu len %zu", id, handle_to_name(handle), handle, (unsigned long long)off, len); @@ -797,20 +848,22 @@ process_write(u_int32_t id) status = SSH2_FX_FAILURE; else { if (!(handle_to_flags(handle) & O_APPEND) && - lseek(fd, off, SEEK_SET) == -1) { + lseek(fd, off, SEEK_SET) == -1) { status = errno_to_portable(errno); - error("process_write: seek failed"); + error_f("seek \"%.100s\": %s", handle_to_name(handle), + strerror(errno)); } else { /* XXX ATOMICIO ? */ ret = write(fd, data, len); if (ret == -1) { - error("process_write: write failed"); status = errno_to_portable(errno); + error_f("write \"%.100s\": %s", + handle_to_name(handle), strerror(errno)); } else if ((size_t)ret == len) { status = SSH2_FX_OK; handle_update_write(handle, ret); } else { - debug2("nothing at all written"); + debug2_f("nothing at all written"); status = SSH2_FX_FAILURE; } } @@ -828,7 +881,7 @@ process_do_stat(u_int32_t id, int do_lstat) int r, status = SSH2_FX_FAILURE; if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "parse"); debug3("request %u: %sstat", id, do_lstat ? "l" : ""); verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); @@ -865,7 +918,7 @@ process_fstat(u_int32_t id) int fd, r, handle, status = SSH2_FX_FAILURE; if ((r = get_handle(iqueue, &handle)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "parse"); debug("request %u: fstat \"%s\" (handle %u)", id, handle_to_name(handle), handle); fd = handle_to_fd(handle); @@ -916,7 +969,7 @@ process_setstat(u_int32_t id) if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || (r = decode_attrib(iqueue, &a)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "parse"); debug("request %u: setstat name \"%s\"", id, name); if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { @@ -963,7 +1016,7 @@ process_fsetstat(u_int32_t id) if ((r = get_handle(iqueue, &handle)) != 0 || (r = decode_attrib(iqueue, &a)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "parse"); debug("request %u: fsetstat handle %d", id, handle); fd = handle_to_fd(handle); @@ -1027,7 +1080,7 @@ process_opendir(u_int32_t id) int r, handle, status = SSH2_FX_FAILURE; if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "parse"); debug3("request %u: opendir", id); logit("opendir \"%s\"", path); @@ -1058,7 +1111,7 @@ process_readdir(u_int32_t id) int r, handle; if ((r = get_handle(iqueue, &handle)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "parse"); debug("request %u: readdir \"%s\" (handle %d)", id, handle_to_name(handle), handle); @@ -1112,7 +1165,7 @@ process_remove(u_int32_t id) int r, status = SSH2_FX_FAILURE; if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "parse"); debug3("request %u: remove", id); logit("remove name \"%s\"", name); @@ -1131,7 +1184,7 @@ process_mkdir(u_int32_t id) if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || (r = decode_attrib(iqueue, &a)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "parse"); mode = (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a.perm & 07777 : 0777; @@ -1150,7 +1203,7 @@ process_rmdir(u_int32_t id) int r, status; if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "parse"); debug3("request %u: rmdir", id); logit("rmdir name \"%s\"", name); @@ -1168,7 +1221,7 @@ process_realpath(u_int32_t id) int r; if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "parse"); if (path[0] == '\0') { free(path); @@ -1196,7 +1249,7 @@ process_rename(u_int32_t id) 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)); + fatal_fr(r, "parse"); debug3("request %u: rename", id); logit("rename old \"%s\" new \"%s\"", oldpath, newpath); @@ -1255,7 +1308,7 @@ process_readlink(u_int32_t id) char *path; if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "parse"); debug3("request %u: readlink", id); verbose("readlink \"%s\"", path); @@ -1280,7 +1333,7 @@ process_symlink(u_int32_t id) 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)); + fatal_fr(r, "parse"); debug3("request %u: symlink", id); logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); @@ -1300,7 +1353,7 @@ process_extended_posix_rename(u_int32_t id) 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)); + fatal_fr(r, "parse"); debug3("request %u: posix-rename", id); logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); @@ -1319,7 +1372,7 @@ process_extended_statvfs(u_int32_t id) int r; if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "parse"); debug3("request %u: statvfs", id); logit("statvfs \"%s\"", path); @@ -1327,7 +1380,7 @@ process_extended_statvfs(u_int32_t id) send_status(id, errno_to_portable(errno)); else send_statvfs(id, &st); - free(path); + free(path); } static void @@ -1337,7 +1390,7 @@ process_extended_fstatvfs(u_int32_t id) struct statvfs st; if ((r = get_handle(iqueue, &handle)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "parse"); debug("request %u: fstatvfs \"%s\" (handle %u)", id, handle_to_name(handle), handle); if ((fd = handle_to_fd(handle)) < 0) { @@ -1358,7 +1411,7 @@ process_extended_hardlink(u_int32_t id) 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)); + fatal_fr(r, "parse"); debug3("request %u: hardlink", id); logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath); @@ -1375,7 +1428,7 @@ process_extended_fsync(u_int32_t id) int handle, fd, r, status = SSH2_FX_OP_UNSUPPORTED; if ((r = get_handle(iqueue, &handle)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "parse"); debug3("request %u: fsync (handle %u)", id, handle); verbose("fsync \"%s\"", handle_to_name(handle)); if ((fd = handle_to_fd(handle)) < 0) @@ -1396,7 +1449,7 @@ process_extended_lsetstat(u_int32_t id) if ((r = sshbuf_get_cstring(iqueue, &name, NULL)) != 0 || (r = decode_attrib(iqueue, &a)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "parse"); debug("request %u: lsetstat name \"%s\"", id, name); if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { @@ -1437,25 +1490,113 @@ process_extended_lsetstat(u_int32_t id) } static void +process_extended_limits(u_int32_t id) +{ + struct sshbuf *msg; + int r; + uint64_t nfiles = 0; +#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) + struct rlimit rlim; +#endif + + debug("request %u: limits", id); + +#if defined(HAVE_GETRLIMIT) && defined(RLIMIT_NOFILE) + if (getrlimit(RLIMIT_NOFILE, &rlim) != -1 && rlim.rlim_cur > 5) + nfiles = rlim.rlim_cur - 5; /* stdio(3) + syslog + spare */ +#endif + + if ((msg = sshbuf_new()) == NULL) + fatal_f("sshbuf_new failed"); + if ((r = sshbuf_put_u8(msg, SSH2_FXP_EXTENDED_REPLY)) != 0 || + (r = sshbuf_put_u32(msg, id)) != 0 || + /* max-packet-length */ + (r = sshbuf_put_u64(msg, SFTP_MAX_MSG_LENGTH)) != 0 || + /* max-read-length */ + (r = sshbuf_put_u64(msg, SFTP_MAX_READ_LENGTH)) != 0 || + /* max-write-length */ + (r = sshbuf_put_u64(msg, SFTP_MAX_MSG_LENGTH - 1024)) != 0 || + /* max-open-handles */ + (r = sshbuf_put_u64(msg, nfiles)) != 0) + fatal_fr(r, "compose"); + send_msg(msg); + sshbuf_free(msg); +} + +static void +process_extended_expand(u_int32_t id) +{ + char cwd[PATH_MAX], resolvedname[PATH_MAX]; + char *path, *npath; + int r; + Stat s; + + if ((r = sshbuf_get_cstring(iqueue, &path, NULL)) != 0) + fatal_fr(r, "parse"); + if (getcwd(cwd, sizeof(cwd)) == NULL) { + send_status(id, errno_to_portable(errno)); + goto out; + } + + debug3("request %u: expand, original \"%s\"", id, path); + if (path[0] == '\0') { + /* empty path */ + free(path); + path = xstrdup("."); + } else if (*path == '~') { + /* ~ expand path */ + /* Special-case for "~" and "~/" to respect homedir flag */ + if (strcmp(path, "~") == 0) { + free(path); + path = xstrdup(cwd); + } else if (strncmp(path, "~/", 2) == 0) { + npath = xstrdup(path + 2); + free(path); + xasprintf(&path, "%s/%s", cwd, npath); + } else { + /* ~user expansions */ + if (tilde_expand(path, pw->pw_uid, &npath) != 0) { + send_status(id, errno_to_portable(EINVAL)); + goto out; + } + free(path); + path = npath; + } + } else if (*path != '/') { + /* relative path */ + xasprintf(&npath, "%s/%s", cwd, path); + free(path); + path = npath; + } + verbose("expand \"%s\"", path); + if (sftp_realpath(path, resolvedname) == NULL) { + send_status(id, errno_to_portable(errno)); + goto out; + } + attrib_clear(&s.attrib); + s.name = s.long_name = resolvedname; + send_names(id, 1, &s); + out: + free(path); +} + +static void process_extended(u_int32_t id) { char *request; - int i, r; + int r; + const struct sftp_handler *exthand; 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) { + fatal_fr(r, "parse"); + if ((exthand = extended_handler_byname(request)) == NULL) { error("Unknown extended request \"%.100s\"", request); send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ + } else { + if (!request_permitted(exthand)) + send_status(id, SSH2_FX_PERMISSION_DENIED); + else + exthand->handler(id); } free(request); } @@ -1486,10 +1627,10 @@ process(void) if (buf_len < msg_len + 4) return; if ((r = sshbuf_consume(iqueue, 4)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "consume"); buf_len -= 4; if ((r = sshbuf_get_u8(iqueue, &type)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "parse type"); switch (type) { case SSH2_FXP_INIT: @@ -1500,14 +1641,14 @@ process(void) 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)); + fatal_fr(r, "parse extended ID"); process_extended(id); break; default: 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)); + fatal_fr(r, "parse ID"); for (i = 0; handlers[i].handler != NULL; i++) { if (type == handlers[i].type) { if (!request_permitted(&handlers[i])) { @@ -1534,7 +1675,7 @@ process(void) } if (msg_len > consumed && (r = sshbuf_consume(iqueue, msg_len - consumed)) != 0) - fatal("%s: buffer error: %s", __func__, ssh_err(r)); + fatal_fr(r, "consume"); } /* Cleanup handler that logs active handles upon normal exit */ @@ -1556,8 +1697,8 @@ sftp_server_usage(void) fprintf(stderr, "usage: %s [-ehR] [-d start_directory] [-f log_facility] " - "[-l log_level]\n\t[-P blacklisted_requests] " - "[-p whitelisted_requests] [-u umask]\n" + "[-l log_level]\n\t[-P denied_requests] " + "[-p allowed_requests] [-u umask]\n" " %s -Q protocol_feature\n", __progname, __progname); exit(1); @@ -1627,14 +1768,14 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) free(cp); break; case 'p': - if (request_whitelist != NULL) + if (request_allowlist != NULL) fatal("Permitted requests already set"); - request_whitelist = xstrdup(optarg); + request_allowlist = xstrdup(optarg); break; case 'P': - if (request_blacklist != NULL) + if (request_denylist != NULL) fatal("Refused requests already set"); - request_blacklist = xstrdup(optarg); + request_denylist = xstrdup(optarg); break; case 'u': errno = 0; @@ -1692,9 +1833,9 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) max = out; if ((iqueue = sshbuf_new()) == NULL) - fatal("%s: sshbuf_new failed", __func__); + fatal_f("sshbuf_new failed"); if ((oqueue = sshbuf_new()) == NULL) - fatal("%s: sshbuf_new failed", __func__); + fatal_f("sshbuf_new failed"); rset = xcalloc(howmany(max + 1, NFDBITS), sizeof(fd_mask)); wset = xcalloc(howmany(max + 1, NFDBITS), sizeof(fd_mask)); @@ -1721,8 +1862,7 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) 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)); + fatal_fr(r, "reserve"); olen = sshbuf_len(oqueue); if (olen > 0) @@ -1744,10 +1884,8 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) } else if (len == -1) { error("read: %s", strerror(errno)); sftp_server_cleanup_exit(1); - } else if ((r = sshbuf_put(iqueue, buf, len)) != 0) { - fatal("%s: buffer error: %s", - __func__, ssh_err(r)); - } + } else if ((r = sshbuf_put(iqueue, buf, len)) != 0) + fatal_fr(r, "sshbuf_put"); } /* send oqueue to stdout */ if (FD_ISSET(out, wset)) { @@ -1755,10 +1893,8 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) if (len == -1) { error("write: %s", strerror(errno)); sftp_server_cleanup_exit(1); - } else if ((r = sshbuf_consume(oqueue, len)) != 0) { - fatal("%s: buffer error: %s", - __func__, ssh_err(r)); - } + } else if ((r = sshbuf_consume(oqueue, len)) != 0) + fatal_fr(r, "consume"); } /* @@ -1770,7 +1906,6 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) if (r == 0) process(); else if (r != SSH_ERR_NO_BUFFER_SPACE) - fatal("%s: sshbuf_check_reserve: %s", - __func__, ssh_err(r)); + fatal_fr(r, "reserve"); } } |