- djm@cvs.openbsd.org 2013/10/09 23:42:17

[sftp-server.8 sftp-server.c]
     Add ability to whitelist and/or blacklist sftp protocol requests by name.
     Refactor dispatch loop and consolidate read-only mode checks.
     Make global variables static, since sftp-server is linked into sshd(8).
     ok dtucker@
This commit is contained in:
Damien Miller 2013-10-15 11:55:57 +11:00
parent df62d71e64
commit 6eaeebf27d
3 changed files with 258 additions and 192 deletions

View File

@ -1,3 +1,12 @@
20131015
- (djm) OpenBSD CVS Sync
- djm@cvs.openbsd.org 2013/10/09 23:42:17
[sftp-server.8 sftp-server.c]
Add ability to whitelist and/or blacklist sftp protocol requests by name.
Refactor dispatch loop and consolidate read-only mode checks.
Make global variables static, since sftp-server is linked into sshd(8).
ok dtucker@
20131010 20131010
- (dtucker) OpenBSD CVS Sync - (dtucker) OpenBSD CVS Sync
- sthen@cvs.openbsd.org 2013/09/16 11:35:43 - sthen@cvs.openbsd.org 2013/09/16 11:35:43

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: sftp-server.8,v 1.23 2013/07/16 00:07:52 schwarze Exp $ .\" $OpenBSD: sftp-server.8,v 1.24 2013/10/09 23:42:17 djm Exp $
.\" .\"
.\" Copyright (c) 2000 Markus Friedl. All rights reserved. .\" Copyright (c) 2000 Markus Friedl. All rights reserved.
.\" .\"
@ -22,7 +22,7 @@
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\" .\"
.Dd $Mdocdate: July 16 2013 $ .Dd $Mdocdate: October 9 2013 $
.Dt SFTP-SERVER 8 .Dt SFTP-SERVER 8
.Os .Os
.Sh NAME .Sh NAME
@ -30,11 +30,15 @@
.Nd SFTP server subsystem .Nd SFTP server subsystem
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm sftp-server .Nm sftp-server
.Bk -words
.Op Fl ehR .Op Fl ehR
.Op Fl d Ar start_directory .Op Fl d Ar start_directory
.Op Fl f Ar log_facility .Op Fl f Ar log_facility
.Op Fl l Ar log_level .Op Fl l Ar log_level
.Op Fl u Ar umask .Op Fl u Ar umask
.Ek
.Nm
.Fl Q Ar protocol_feature
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm
is a program that speaks the server side of SFTP protocol is a program that speaks the server side of SFTP protocol
@ -93,6 +97,36 @@ performs on behalf of the client.
DEBUG and DEBUG1 are equivalent. DEBUG and DEBUG1 are equivalent.
DEBUG2 and DEBUG3 each specify higher levels of debugging output. DEBUG2 and DEBUG3 each specify higher levels of debugging output.
The default is ERROR. The default is ERROR.
.It Fl P Ar blacklisted_requests
Specify a comma-separated list of sftp protocol requests that are banned by
the server.
.Nm
will reply to any blacklisted request with a failure.
The
.Fl Q
flag allows querying
.Nm
to determine the supported request types.
If both a blacklist and a whitelist are specified, then the blacklist is
applied before the whitelist.
.It Fl p Ar whitelisted_requests
Specify a comma-separated list of sftp protocol requests that are permitted
by the server.
All request types that are not on the whitelist will be logged and replied
to with a failure message.
.Pp
Care must be taken when using this feature to ensure that requests made
implicitly by sftp clients are permitted.
.It Fl Q Ar protocol_feature
Query protocol features supported by
.Nm .
At present the only feature that may be queried is
.Dq requests ,
that may be used for whitelisting or blacklisting (flags
.Fl p
and
.Fl P
respectively.)
.It Fl R .It Fl R
Places this instance of Places this instance of
.Nm .Nm

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sftp-server.c,v 1.97 2013/05/17 00:13:14 djm Exp $ */ /* $OpenBSD: sftp-server.c,v 1.98 2013/10/09 23:42:17 djm Exp $ */
/* /*
* Copyright (c) 2000-2004 Markus Friedl. All rights reserved. * Copyright (c) 2000-2004 Markus Friedl. All rights reserved.
* *
@ -46,6 +46,7 @@
#include "buffer.h" #include "buffer.h"
#include "log.h" #include "log.h"
#include "misc.h" #include "misc.h"
#include "match.h"
#include "uidswap.h" #include "uidswap.h"
#include "sftp.h" #include "sftp.h"
@ -57,24 +58,29 @@
#define get_string(lenp) buffer_get_string(&iqueue, lenp); #define get_string(lenp) buffer_get_string(&iqueue, lenp);
/* Our verbosity */ /* Our verbosity */
LogLevel log_level = SYSLOG_LEVEL_ERROR; static LogLevel log_level = SYSLOG_LEVEL_ERROR;
/* Our client */ /* Our client */
struct passwd *pw = NULL; static struct passwd *pw = NULL;
char *client_addr = NULL; static char *client_addr = NULL;
/* input and output queue */ /* input and output queue */
Buffer iqueue; static Buffer iqueue;
Buffer oqueue; static Buffer oqueue;
/* Version of client */ /* Version of client */
u_int version; static u_int version;
/* SSH2_FXP_INIT received */
static int init_done;
/* Disable writes */ /* Disable writes */
int readonly; static int readonly;
/* Requests that are allowed/denied */
static char *request_whitelist, *request_blacklist;
/* portable attributes, etc. */ /* portable attributes, etc. */
typedef struct Stat Stat; typedef struct Stat Stat;
struct Stat { struct Stat {
@ -83,6 +89,100 @@ struct Stat {
Attrib attrib; 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(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 },
{ 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 static int
errno_to_portable(int unixerrno) errno_to_portable(int unixerrno)
{ {
@ -543,14 +643,13 @@ process_init(void)
} }
static void static void
process_open(void) process_open(u_int32_t id)
{ {
u_int32_t id, pflags; u_int32_t pflags;
Attrib *a; Attrib *a;
char *name; char *name;
int handle, fd, flags, mode, status = SSH2_FX_FAILURE; int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
id = get_int();
name = get_string(NULL); name = get_string(NULL);
pflags = get_int(); /* portable flags */ pflags = get_int(); /* portable flags */
debug3("request %u: open flags %d", id, pflags); debug3("request %u: open flags %d", id, pflags);
@ -560,9 +659,11 @@ process_open(void)
logit("open \"%s\" flags %s mode 0%o", logit("open \"%s\" flags %s mode 0%o",
name, string_from_portable(pflags), mode); name, string_from_portable(pflags), mode);
if (readonly && if (readonly &&
((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR)) ((flags & O_ACCMODE) == O_WRONLY ||
status = SSH2_FX_PERMISSION_DENIED; (flags & O_ACCMODE) == O_RDWR)) {
else { verbose("Refusing open request in read-only mode");
status = SSH2_FX_PERMISSION_DENIED;
} else {
fd = open(name, flags, mode); fd = open(name, flags, mode);
if (fd < 0) { if (fd < 0) {
status = errno_to_portable(errno); status = errno_to_portable(errno);
@ -582,12 +683,10 @@ process_open(void)
} }
static void static void
process_close(void) process_close(u_int32_t id)
{ {
u_int32_t id;
int handle, ret, status = SSH2_FX_FAILURE; int handle, ret, status = SSH2_FX_FAILURE;
id = get_int();
handle = get_handle(); handle = get_handle();
debug3("request %u: close handle %u", id, handle); debug3("request %u: close handle %u", id, handle);
handle_log_close(handle, NULL); handle_log_close(handle, NULL);
@ -597,14 +696,13 @@ process_close(void)
} }
static void static void
process_read(void) process_read(u_int32_t id)
{ {
char buf[64*1024]; char buf[64*1024];
u_int32_t id, len; u_int32_t len;
int handle, fd, ret, status = SSH2_FX_FAILURE; int handle, fd, ret, status = SSH2_FX_FAILURE;
u_int64_t off; u_int64_t off;
id = get_int();
handle = get_handle(); handle = get_handle();
off = get_int64(); off = get_int64();
len = get_int(); len = get_int();
@ -638,15 +736,13 @@ process_read(void)
} }
static void static void
process_write(void) process_write(u_int32_t id)
{ {
u_int32_t id;
u_int64_t off; u_int64_t off;
u_int len; u_int len;
int handle, fd, ret, status; int handle, fd, ret, status;
char *data; char *data;
id = get_int();
handle = get_handle(); handle = get_handle();
off = get_int64(); off = get_int64();
data = get_string(&len); data = get_string(&len);
@ -657,8 +753,6 @@ process_write(void)
if (fd < 0) if (fd < 0)
status = SSH2_FX_FAILURE; status = SSH2_FX_FAILURE;
else if (readonly)
status = SSH2_FX_PERMISSION_DENIED;
else { else {
if (lseek(fd, off, SEEK_SET) < 0) { if (lseek(fd, off, SEEK_SET) < 0) {
status = errno_to_portable(errno); status = errno_to_portable(errno);
@ -683,15 +777,13 @@ process_write(void)
} }
static void static void
process_do_stat(int do_lstat) process_do_stat(u_int32_t id, int do_lstat)
{ {
Attrib a; Attrib a;
struct stat st; struct stat st;
u_int32_t id;
char *name; char *name;
int ret, status = SSH2_FX_FAILURE; int ret, status = SSH2_FX_FAILURE;
id = get_int();
name = get_string(NULL); name = get_string(NULL);
debug3("request %u: %sstat", id, do_lstat ? "l" : ""); debug3("request %u: %sstat", id, do_lstat ? "l" : "");
verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name);
@ -709,26 +801,24 @@ process_do_stat(int do_lstat)
} }
static void static void
process_stat(void) process_stat(u_int32_t id)
{ {
process_do_stat(0); process_do_stat(id, 0);
} }
static void static void
process_lstat(void) process_lstat(u_int32_t id)
{ {
process_do_stat(1); process_do_stat(id, 1);
} }
static void static void
process_fstat(void) process_fstat(u_int32_t id)
{ {
Attrib a; Attrib a;
struct stat st; struct stat st;
u_int32_t id;
int fd, ret, handle, status = SSH2_FX_FAILURE; int fd, ret, handle, status = SSH2_FX_FAILURE;
id = get_int();
handle = get_handle(); handle = get_handle();
debug("request %u: fstat \"%s\" (handle %u)", debug("request %u: fstat \"%s\" (handle %u)",
id, handle_to_name(handle), handle); id, handle_to_name(handle), handle);
@ -760,21 +850,15 @@ attrib_to_tv(const Attrib *a)
} }
static void static void
process_setstat(void) process_setstat(u_int32_t id)
{ {
Attrib *a; Attrib *a;
u_int32_t id;
char *name; char *name;
int status = SSH2_FX_OK, ret; int status = SSH2_FX_OK, ret;
id = get_int();
name = get_string(NULL); name = get_string(NULL);
a = get_attrib(); a = get_attrib();
debug("request %u: setstat name \"%s\"", id, name); 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", logit("set \"%s\" size %llu",
name, (unsigned long long)a->size); name, (unsigned long long)a->size);
@ -811,22 +895,18 @@ process_setstat(void)
} }
static void static void
process_fsetstat(void) process_fsetstat(u_int32_t id)
{ {
Attrib *a; Attrib *a;
u_int32_t id;
int handle, fd, ret; int handle, fd, ret;
int status = SSH2_FX_OK; int status = SSH2_FX_OK;
id = get_int();
handle = get_handle(); handle = get_handle();
a = get_attrib(); a = get_attrib();
debug("request %u: fsetstat handle %d", id, handle); debug("request %u: fsetstat handle %d", id, handle);
fd = handle_to_fd(handle); fd = handle_to_fd(handle);
if (fd < 0) if (fd < 0)
status = SSH2_FX_FAILURE; status = SSH2_FX_FAILURE;
else if (readonly)
status = SSH2_FX_PERMISSION_DENIED;
else { else {
char *name = handle_to_name(handle); char *name = handle_to_name(handle);
@ -878,14 +958,12 @@ process_fsetstat(void)
} }
static void static void
process_opendir(void) process_opendir(u_int32_t id)
{ {
DIR *dirp = NULL; DIR *dirp = NULL;
char *path; char *path;
int handle, status = SSH2_FX_FAILURE; int handle, status = SSH2_FX_FAILURE;
u_int32_t id;
id = get_int();
path = get_string(NULL); path = get_string(NULL);
debug3("request %u: opendir", id); debug3("request %u: opendir", id);
logit("opendir \"%s\"", path); logit("opendir \"%s\"", path);
@ -908,15 +986,13 @@ process_opendir(void)
} }
static void static void
process_readdir(void) process_readdir(u_int32_t id)
{ {
DIR *dirp; DIR *dirp;
struct dirent *dp; struct dirent *dp;
char *path; char *path;
int handle; int handle;
u_int32_t id;
id = get_int();
handle = get_handle(); handle = get_handle();
debug("request %u: readdir \"%s\" (handle %d)", id, debug("request %u: readdir \"%s\" (handle %d)", id,
handle_to_name(handle), handle); handle_to_name(handle), handle);
@ -964,81 +1040,61 @@ process_readdir(void)
} }
static void static void
process_remove(void) process_remove(u_int32_t id)
{ {
char *name; char *name;
u_int32_t id;
int status = SSH2_FX_FAILURE; int status = SSH2_FX_FAILURE;
int ret; int ret;
id = get_int();
name = get_string(NULL); name = get_string(NULL);
debug3("request %u: remove", id); debug3("request %u: remove", id);
logit("remove name \"%s\"", name); logit("remove name \"%s\"", name);
if (readonly) ret = unlink(name);
status = SSH2_FX_PERMISSION_DENIED; status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
else {
ret = unlink(name);
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
}
send_status(id, status); send_status(id, status);
free(name); free(name);
} }
static void static void
process_mkdir(void) process_mkdir(u_int32_t id)
{ {
Attrib *a; Attrib *a;
u_int32_t id;
char *name; char *name;
int ret, mode, status = SSH2_FX_FAILURE; int ret, mode, status = SSH2_FX_FAILURE;
id = get_int();
name = get_string(NULL); name = get_string(NULL);
a = get_attrib(); a = get_attrib();
mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
a->perm & 07777 : 0777; a->perm & 07777 : 0777;
debug3("request %u: mkdir", id); debug3("request %u: mkdir", id);
logit("mkdir name \"%s\" mode 0%o", name, mode); logit("mkdir name \"%s\" mode 0%o", name, mode);
if (readonly) ret = mkdir(name, mode);
status = SSH2_FX_PERMISSION_DENIED; status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
else {
ret = mkdir(name, mode);
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
}
send_status(id, status); send_status(id, status);
free(name); free(name);
} }
static void static void
process_rmdir(void) process_rmdir(u_int32_t id)
{ {
u_int32_t id;
char *name; char *name;
int ret, status; int ret, status;
id = get_int();
name = get_string(NULL); name = get_string(NULL);
debug3("request %u: rmdir", id); debug3("request %u: rmdir", id);
logit("rmdir name \"%s\"", name); logit("rmdir name \"%s\"", name);
if (readonly) ret = rmdir(name);
status = SSH2_FX_PERMISSION_DENIED; status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
else {
ret = rmdir(name);
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
}
send_status(id, status); send_status(id, status);
free(name); free(name);
} }
static void static void
process_realpath(void) process_realpath(u_int32_t id)
{ {
char resolvedname[MAXPATHLEN]; char resolvedname[MAXPATHLEN];
u_int32_t id;
char *path; char *path;
id = get_int();
path = get_string(NULL); path = get_string(NULL);
if (path[0] == '\0') { if (path[0] == '\0') {
free(path); free(path);
@ -1058,22 +1114,18 @@ process_realpath(void)
} }
static void static void
process_rename(void) process_rename(u_int32_t id)
{ {
u_int32_t id;
char *oldpath, *newpath; char *oldpath, *newpath;
int status; int status;
struct stat sb; struct stat sb;
id = get_int();
oldpath = get_string(NULL); oldpath = get_string(NULL);
newpath = get_string(NULL); newpath = get_string(NULL);
debug3("request %u: rename", id); debug3("request %u: rename", id);
logit("rename old \"%s\" new \"%s\"", oldpath, newpath); logit("rename old \"%s\" new \"%s\"", oldpath, newpath);
status = SSH2_FX_FAILURE; status = SSH2_FX_FAILURE;
if (readonly) if (lstat(oldpath, &sb) == -1)
status = SSH2_FX_PERMISSION_DENIED;
else if (lstat(oldpath, &sb) == -1)
status = errno_to_portable(errno); status = errno_to_portable(errno);
else if (S_ISREG(sb.st_mode)) { else if (S_ISREG(sb.st_mode)) {
/* Race-free rename of regular files */ /* Race-free rename of regular files */
@ -1120,14 +1172,12 @@ process_rename(void)
} }
static void static void
process_readlink(void) process_readlink(u_int32_t id)
{ {
u_int32_t id;
int len; int len;
char buf[MAXPATHLEN]; char buf[MAXPATHLEN];
char *path; char *path;
id = get_int();
path = get_string(NULL); path = get_string(NULL);
debug3("request %u: readlink", id); debug3("request %u: readlink", id);
verbose("readlink \"%s\"", path); verbose("readlink \"%s\"", path);
@ -1145,24 +1195,18 @@ process_readlink(void)
} }
static void static void
process_symlink(void) process_symlink(u_int32_t id)
{ {
u_int32_t id;
char *oldpath, *newpath; char *oldpath, *newpath;
int ret, status; int ret, status;
id = get_int();
oldpath = get_string(NULL); oldpath = get_string(NULL);
newpath = get_string(NULL); newpath = get_string(NULL);
debug3("request %u: symlink", id); debug3("request %u: symlink", id);
logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); logit("symlink old \"%s\" new \"%s\"", oldpath, newpath);
/* this will fail if 'newpath' exists */ /* this will fail if 'newpath' exists */
if (readonly) ret = symlink(oldpath, newpath);
status = SSH2_FX_PERMISSION_DENIED; status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
else {
ret = symlink(oldpath, newpath);
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
}
send_status(id, status); send_status(id, status);
free(oldpath); free(oldpath);
free(newpath); free(newpath);
@ -1178,12 +1222,8 @@ process_extended_posix_rename(u_int32_t id)
newpath = get_string(NULL); newpath = get_string(NULL);
debug3("request %u: posix-rename", id); debug3("request %u: posix-rename", id);
logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath);
if (readonly) ret = rename(oldpath, newpath);
status = SSH2_FX_PERMISSION_DENIED; status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
else {
ret = rename(oldpath, newpath);
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
}
send_status(id, status); send_status(id, status);
free(oldpath); free(oldpath);
free(newpath); free(newpath);
@ -1235,35 +1275,33 @@ process_extended_hardlink(u_int32_t id)
newpath = get_string(NULL); newpath = get_string(NULL);
debug3("request %u: hardlink", id); debug3("request %u: hardlink", id);
logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath); logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath);
if (readonly) ret = link(oldpath, newpath);
status = SSH2_FX_PERMISSION_DENIED; status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
else {
ret = link(oldpath, newpath);
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
}
send_status(id, status); send_status(id, status);
free(oldpath); free(oldpath);
free(newpath); free(newpath);
} }
static void static void
process_extended(void) process_extended(u_int32_t id)
{ {
u_int32_t id;
char *request; char *request;
u_int i;
id = get_int();
request = get_string(NULL); request = get_string(NULL);
if (strcmp(request, "posix-rename@openssh.com") == 0) for (i = 0; extended_handlers[i].handler != NULL; i++) {
process_extended_posix_rename(id); if (strcmp(request, extended_handlers[i].ext_name) == 0) {
else if (strcmp(request, "statvfs@openssh.com") == 0) if (!request_permitted(&extended_handlers[i]))
process_extended_statvfs(id); send_status(id, SSH2_FX_PERMISSION_DENIED);
else if (strcmp(request, "fstatvfs@openssh.com") == 0) else
process_extended_fstatvfs(id); extended_handlers[i].handler(id);
else if (strcmp(request, "hardlink@openssh.com") == 0) break;
process_extended_hardlink(id); }
else }
if (extended_handlers[i].handler == NULL) {
error("Unknown extended request \"%.100s\"", request);
send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
}
free(request); free(request);
} }
@ -1272,11 +1310,9 @@ process_extended(void)
static void static void
process(void) process(void)
{ {
u_int msg_len; u_int msg_len, buf_len, consumed, type, i;
u_int buf_len;
u_int consumed;
u_int type;
u_char *cp; u_char *cp;
u_int32_t id;
buf_len = buffer_len(&iqueue); buf_len = buffer_len(&iqueue);
if (buf_len < 5) if (buf_len < 5)
@ -1293,70 +1329,35 @@ process(void)
buffer_consume(&iqueue, 4); buffer_consume(&iqueue, 4);
buf_len -= 4; buf_len -= 4;
type = buffer_get_char(&iqueue); type = buffer_get_char(&iqueue);
switch (type) { switch (type) {
case SSH2_FXP_INIT: case SSH2_FXP_INIT:
process_init(); process_init();
break; init_done = 1;
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();
break; break;
case SSH2_FXP_EXTENDED: case SSH2_FXP_EXTENDED:
process_extended(); if (!init_done)
fatal("Received extended request before init");
id = get_int();
process_extended(id);
break; break;
default: default:
error("Unknown message %d", type); if (!init_done)
break; fatal("Received %u request before init", type);
id = get_int();
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 */ /* discard the remaining bytes from the current packet */
if (buf_len < buffer_len(&iqueue)) { if (buf_len < buffer_len(&iqueue)) {
@ -1365,7 +1366,7 @@ process(void)
} }
consumed = buf_len - buffer_len(&iqueue); consumed = buf_len - buffer_len(&iqueue);
if (msg_len < consumed) { 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); sftp_server_cleanup_exit(255);
} }
if (msg_len > consumed) if (msg_len > consumed)
@ -1400,7 +1401,7 @@ int
sftp_server_main(int argc, char **argv, struct passwd *user_pw) sftp_server_main(int argc, char **argv, struct passwd *user_pw)
{ {
fd_set *rset, *wset; fd_set *rset, *wset;
int in, out, max, ch, skipargs = 0, log_stderr = 0; int i, in, out, max, ch, skipargs = 0, log_stderr = 0;
ssize_t len, olen, set_size; ssize_t len, olen, set_size;
SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
char *cp, *homedir = NULL, buf[4*4096]; char *cp, *homedir = NULL, buf[4*4096];
@ -1414,8 +1415,20 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw)
pw = pwcopy(user_pw); pw = pwcopy(user_pw);
while (!skipargs && (ch = getopt(argc, argv, "d:f:l:u:cehR")) != -1) { while (!skipargs && (ch = getopt(argc, argv,
"d:f:l:P:p:Q:u:cehR")) != -1) {
switch (ch) { 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': case 'R':
readonly = 1; readonly = 1;
break; break;
@ -1445,6 +1458,16 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw)
"u", user_pw->pw_name, (char *)NULL); "u", user_pw->pw_name, (char *)NULL);
free(cp); free(cp);
break; 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': case 'u':
errno = 0; errno = 0;
mask = strtol(optarg, &cp, 8); mask = strtol(optarg, &cp, 8);