- djm@cvs.openbsd.org 2010/01/09 00:20:26

[sftp-server.c sftp-server.8]
     add a 'read-only' mode to sftp-server(8) that disables open in write mode
     and all other fs-modifying protocol methods. bz#430 ok dtucker@
This commit is contained in:
Darren Tucker 2010-01-09 22:24:33 +11:00
parent 0b8a2262ac
commit db7bf82544
3 changed files with 88 additions and 32 deletions

View File

@ -5,6 +5,11 @@
- (dtucker) [roaming_client.c] Wrap inttypes.h in an ifdef. - (dtucker) [roaming_client.c] Wrap inttypes.h in an ifdef.
- (dtucker) [loginrec.c] Use the SUSv3 specified name for the user name - (dtucker) [loginrec.c] Use the SUSv3 specified name for the user name
when using utmpx. Patch from Ed Schouten. when using utmpx. Patch from Ed Schouten.
- (dtucker) OpenBSD CVS Sync
- djm@cvs.openbsd.org 2010/01/09 00:20:26
[sftp-server.c sftp-server.8]
add a 'read-only' mode to sftp-server(8) that disables open in write mode
and all other fs-modifying protocol methods. bz#430 ok dtucker@
20091208 20091208
- (dtucker) OpenBSD CVS Sync - (dtucker) OpenBSD CVS Sync

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: sftp-server.8,v 1.17 2009/08/31 21:01:29 djm Exp $ .\" $OpenBSD: sftp-server.8,v 1.18 2010/01/09 00:20:26 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: August 31 2009 $ .Dd $Mdocdate: January 9 2010 $
.Dt SFTP-SERVER 8 .Dt SFTP-SERVER 8
.Os .Os
.Sh NAME .Sh NAME
@ -30,7 +30,7 @@
.Nd SFTP server subsystem .Nd SFTP server subsystem
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm sftp-server .Nm sftp-server
.Op Fl eh .Op Fl ehR
.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
@ -81,6 +81,12 @@ 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 R
Places this instance of
.Nm
into a read-only mode.
Attempts to open files for writing, as well as other operations that change
the state of the filesystem will be denied.
.It Fl u Ar umask .It Fl u Ar umask
Sets an explicit Sets an explicit
.Xr umask 2 .Xr umask 2

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sftp-server.c,v 1.89 2010/01/04 02:25:15 djm Exp $ */ /* $OpenBSD: sftp-server.c,v 1.90 2010/01/09 00:20:26 djm Exp $ */
/* /*
* Copyright (c) 2000-2004 Markus Friedl. All rights reserved. * Copyright (c) 2000-2004 Markus Friedl. All rights reserved.
* *
@ -70,6 +70,9 @@ Buffer oqueue;
/* Version of client */ /* Version of client */
int version; int version;
/* Disable writes */
int readonly;
/* portable attributes, etc. */ /* portable attributes, etc. */
typedef struct Stat Stat; typedef struct Stat Stat;
@ -553,16 +556,21 @@ process_open(void)
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", logit("open \"%s\" flags %s mode 0%o",
name, string_from_portable(pflags), mode); name, string_from_portable(pflags), mode);
fd = open(name, flags, mode); if (readonly &&
if (fd < 0) { ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR))
status = errno_to_portable(errno); status = SSH2_FX_PERMISSION_DENIED;
} else { else {
handle = handle_new(HANDLE_FILE, name, fd, NULL); fd = open(name, flags, mode);
if (handle < 0) { if (fd < 0) {
close(fd); status = errno_to_portable(errno);
} else { } else {
send_handle(id, handle); handle = handle_new(HANDLE_FILE, name, fd, NULL);
status = SSH2_FX_OK; if (handle < 0) {
close(fd);
} else {
send_handle(id, handle);
status = SSH2_FX_OK;
}
} }
} }
if (status != SSH2_FX_OK) if (status != SSH2_FX_OK)
@ -632,7 +640,7 @@ process_write(void)
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 = SSH2_FX_FAILURE; int handle, fd, ret, status;
char *data; char *data;
id = get_int(); id = get_int();
@ -643,7 +651,12 @@ process_write(void)
debug("request %u: write \"%s\" (handle %d) off %llu len %d", debug("request %u: write \"%s\" (handle %d) off %llu len %d",
id, handle_to_name(handle), handle, (unsigned long long)off, len); id, handle_to_name(handle), handle, (unsigned long long)off, len);
fd = handle_to_fd(handle); fd = handle_to_fd(handle);
if (fd >= 0) {
if (fd < 0)
status = SSH2_FX_FAILURE;
else if (readonly)
status = SSH2_FX_PERMISSION_DENIED;
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);
error("process_write: seek failed"); error("process_write: seek failed");
@ -658,6 +671,7 @@ process_write(void)
handle_update_write(handle, ret); handle_update_write(handle, ret);
} else { } else {
debug2("nothing at all written"); debug2("nothing at all written");
status = SSH2_FX_FAILURE;
} }
} }
} }
@ -754,6 +768,10 @@ process_setstat(void)
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);
@ -802,9 +820,11 @@ process_fsetstat(void)
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 { else if (readonly)
status = SSH2_FX_PERMISSION_DENIED;
else {
char *name = handle_to_name(handle); char *name = handle_to_name(handle);
if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
@ -952,8 +972,12 @@ process_remove(void)
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);
ret = unlink(name); if (readonly)
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; status = SSH2_FX_PERMISSION_DENIED;
else {
ret = unlink(name);
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
}
send_status(id, status); send_status(id, status);
xfree(name); xfree(name);
} }
@ -973,8 +997,12 @@ process_mkdir(void)
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);
ret = mkdir(name, mode); if (readonly)
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; status = SSH2_FX_PERMISSION_DENIED;
else {
ret = mkdir(name, mode);
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
}
send_status(id, status); send_status(id, status);
xfree(name); xfree(name);
} }
@ -990,8 +1018,12 @@ process_rmdir(void)
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);
ret = rmdir(name); if (readonly)
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; status = SSH2_FX_PERMISSION_DENIED;
else {
ret = rmdir(name);
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
}
send_status(id, status); send_status(id, status);
xfree(name); xfree(name);
} }
@ -1036,7 +1068,9 @@ process_rename(void)
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 (lstat(oldpath, &sb) == -1) if (readonly)
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,8 +1154,12 @@ process_symlink(void)
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 */
ret = symlink(oldpath, newpath); if (readonly)
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; status = SSH2_FX_PERMISSION_DENIED;
else {
ret = symlink(oldpath, newpath);
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
}
send_status(id, status); send_status(id, status);
xfree(oldpath); xfree(oldpath);
xfree(newpath); xfree(newpath);
@ -1131,15 +1169,19 @@ static void
process_extended_posix_rename(u_int32_t id) process_extended_posix_rename(u_int32_t id)
{ {
char *oldpath, *newpath; char *oldpath, *newpath;
int ret, status;
oldpath = get_string(NULL); oldpath = get_string(NULL);
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 (rename(oldpath, newpath) == -1) if (readonly)
send_status(id, errno_to_portable(errno)); status = SSH2_FX_PERMISSION_DENIED;
else else {
send_status(id, SSH2_FX_OK); ret = rename(oldpath, newpath);
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
}
send_status(id, status);
xfree(oldpath); xfree(oldpath);
xfree(newpath); xfree(newpath);
} }
@ -1322,7 +1364,7 @@ sftp_server_usage(void)
extern char *__progname; extern char *__progname;
fprintf(stderr, fprintf(stderr,
"usage: %s [-eh] [-f log_facility] [-l log_level] [-u umask]\n", "usage: %s [-ehR] [-f log_facility] [-l log_level] [-u umask]\n",
__progname); __progname);
exit(1); exit(1);
} }
@ -1344,8 +1386,11 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw)
__progname = ssh_get_progname(argv[0]); __progname = ssh_get_progname(argv[0]);
log_init(__progname, log_level, log_facility, log_stderr); log_init(__progname, log_level, log_facility, log_stderr);
while (!skipargs && (ch = getopt(argc, argv, "f:l:u:che")) != -1) { while (!skipargs && (ch = getopt(argc, argv, "f:l:u:cehR")) != -1) {
switch (ch) { switch (ch) {
case 'R':
readonly = 1;
break;
case 'c': case 'c':
/* /*
* Ignore all arguments if we are invoked as a * Ignore all arguments if we are invoked as a