summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--sftp-server.812
-rw-r--r--sftp-server.c103
3 files changed, 88 insertions, 32 deletions
diff --git a/ChangeLog b/ChangeLog
index 31f205f6a..647d71def 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,6 +5,11 @@
5 - (dtucker) [roaming_client.c] Wrap inttypes.h in an ifdef. 5 - (dtucker) [roaming_client.c] Wrap inttypes.h in an ifdef.
6 - (dtucker) [loginrec.c] Use the SUSv3 specified name for the user name 6 - (dtucker) [loginrec.c] Use the SUSv3 specified name for the user name
7 when using utmpx. Patch from Ed Schouten. 7 when using utmpx. Patch from Ed Schouten.
8 - (dtucker) OpenBSD CVS Sync
9 - djm@cvs.openbsd.org 2010/01/09 00:20:26
10 [sftp-server.c sftp-server.8]
11 add a 'read-only' mode to sftp-server(8) that disables open in write mode
12 and all other fs-modifying protocol methods. bz#430 ok dtucker@
8 13
920091208 1420091208
10 - (dtucker) OpenBSD CVS Sync 15 - (dtucker) OpenBSD CVS Sync
diff --git a/sftp-server.8 b/sftp-server.8
index ee73c345b..84036922c 100644
--- a/sftp-server.8
+++ b/sftp-server.8
@@ -1,4 +1,4 @@
1.\" $OpenBSD: sftp-server.8,v 1.17 2009/08/31 21:01:29 djm Exp $ 1.\" $OpenBSD: sftp-server.8,v 1.18 2010/01/09 00:20:26 djm Exp $
2.\" 2.\"
3.\" Copyright (c) 2000 Markus Friedl. All rights reserved. 3.\" Copyright (c) 2000 Markus Friedl. All rights reserved.
4.\" 4.\"
@@ -22,7 +22,7 @@
22.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24.\" 24.\"
25.Dd $Mdocdate: August 31 2009 $ 25.Dd $Mdocdate: January 9 2010 $
26.Dt SFTP-SERVER 8 26.Dt SFTP-SERVER 8
27.Os 27.Os
28.Sh NAME 28.Sh NAME
@@ -30,7 +30,7 @@
30.Nd SFTP server subsystem 30.Nd SFTP server subsystem
31.Sh SYNOPSIS 31.Sh SYNOPSIS
32.Nm sftp-server 32.Nm sftp-server
33.Op Fl eh 33.Op Fl ehR
34.Op Fl f Ar log_facility 34.Op Fl f Ar log_facility
35.Op Fl l Ar log_level 35.Op Fl l Ar log_level
36.Op Fl u Ar umask 36.Op Fl u Ar umask
@@ -81,6 +81,12 @@ performs on behalf of the client.
81DEBUG and DEBUG1 are equivalent. 81DEBUG and DEBUG1 are equivalent.
82DEBUG2 and DEBUG3 each specify higher levels of debugging output. 82DEBUG2 and DEBUG3 each specify higher levels of debugging output.
83The default is ERROR. 83The default is ERROR.
84.It Fl R
85Places this instance of
86.Nm
87into a read-only mode.
88Attempts to open files for writing, as well as other operations that change
89the state of the filesystem will be denied.
84.It Fl u Ar umask 90.It Fl u Ar umask
85Sets an explicit 91Sets an explicit
86.Xr umask 2 92.Xr umask 2
diff --git a/sftp-server.c b/sftp-server.c
index cf4e273f8..ab9391cfd 100644
--- a/sftp-server.c
+++ b/sftp-server.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-server.c,v 1.89 2010/01/04 02:25:15 djm Exp $ */ 1/* $OpenBSD: sftp-server.c,v 1.90 2010/01/09 00:20:26 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000-2004 Markus Friedl. All rights reserved.
4 * 4 *
@@ -70,6 +70,9 @@ Buffer oqueue;
70/* Version of client */ 70/* Version of client */
71int version; 71int version;
72 72
73/* Disable writes */
74int readonly;
75
73/* portable attributes, etc. */ 76/* portable attributes, etc. */
74 77
75typedef struct Stat Stat; 78typedef struct Stat Stat;
@@ -553,16 +556,21 @@ process_open(void)
553 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; 556 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
554 logit("open \"%s\" flags %s mode 0%o", 557 logit("open \"%s\" flags %s mode 0%o",
555 name, string_from_portable(pflags), mode); 558 name, string_from_portable(pflags), mode);
556 fd = open(name, flags, mode); 559 if (readonly &&
557 if (fd < 0) { 560 ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR))
558 status = errno_to_portable(errno); 561 status = SSH2_FX_PERMISSION_DENIED;
559 } else { 562 else {
560 handle = handle_new(HANDLE_FILE, name, fd, NULL); 563 fd = open(name, flags, mode);
561 if (handle < 0) { 564 if (fd < 0) {
562 close(fd); 565 status = errno_to_portable(errno);
563 } else { 566 } else {
564 send_handle(id, handle); 567 handle = handle_new(HANDLE_FILE, name, fd, NULL);
565 status = SSH2_FX_OK; 568 if (handle < 0) {
569 close(fd);
570 } else {
571 send_handle(id, handle);
572 status = SSH2_FX_OK;
573 }
566 } 574 }
567 } 575 }
568 if (status != SSH2_FX_OK) 576 if (status != SSH2_FX_OK)
@@ -632,7 +640,7 @@ process_write(void)
632 u_int32_t id; 640 u_int32_t id;
633 u_int64_t off; 641 u_int64_t off;
634 u_int len; 642 u_int len;
635 int handle, fd, ret, status = SSH2_FX_FAILURE; 643 int handle, fd, ret, status;
636 char *data; 644 char *data;
637 645
638 id = get_int(); 646 id = get_int();
@@ -643,7 +651,12 @@ process_write(void)
643 debug("request %u: write \"%s\" (handle %d) off %llu len %d", 651 debug("request %u: write \"%s\" (handle %d) off %llu len %d",
644 id, handle_to_name(handle), handle, (unsigned long long)off, len); 652 id, handle_to_name(handle), handle, (unsigned long long)off, len);
645 fd = handle_to_fd(handle); 653 fd = handle_to_fd(handle);
646 if (fd >= 0) { 654
655 if (fd < 0)
656 status = SSH2_FX_FAILURE;
657 else if (readonly)
658 status = SSH2_FX_PERMISSION_DENIED;
659 else {
647 if (lseek(fd, off, SEEK_SET) < 0) { 660 if (lseek(fd, off, SEEK_SET) < 0) {
648 status = errno_to_portable(errno); 661 status = errno_to_portable(errno);
649 error("process_write: seek failed"); 662 error("process_write: seek failed");
@@ -658,6 +671,7 @@ process_write(void)
658 handle_update_write(handle, ret); 671 handle_update_write(handle, ret);
659 } else { 672 } else {
660 debug2("nothing at all written"); 673 debug2("nothing at all written");
674 status = SSH2_FX_FAILURE;
661 } 675 }
662 } 676 }
663 } 677 }
@@ -754,6 +768,10 @@ process_setstat(void)
754 name = get_string(NULL); 768 name = get_string(NULL);
755 a = get_attrib(); 769 a = get_attrib();
756 debug("request %u: setstat name \"%s\"", id, name); 770 debug("request %u: setstat name \"%s\"", id, name);
771 if (readonly) {
772 status = SSH2_FX_PERMISSION_DENIED;
773 a->flags = 0;
774 }
757 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 775 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
758 logit("set \"%s\" size %llu", 776 logit("set \"%s\" size %llu",
759 name, (unsigned long long)a->size); 777 name, (unsigned long long)a->size);
@@ -802,9 +820,11 @@ process_fsetstat(void)
802 a = get_attrib(); 820 a = get_attrib();
803 debug("request %u: fsetstat handle %d", id, handle); 821 debug("request %u: fsetstat handle %d", id, handle);
804 fd = handle_to_fd(handle); 822 fd = handle_to_fd(handle);
805 if (fd < 0) { 823 if (fd < 0)
806 status = SSH2_FX_FAILURE; 824 status = SSH2_FX_FAILURE;
807 } else { 825 else if (readonly)
826 status = SSH2_FX_PERMISSION_DENIED;
827 else {
808 char *name = handle_to_name(handle); 828 char *name = handle_to_name(handle);
809 829
810 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 830 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
@@ -952,8 +972,12 @@ process_remove(void)
952 name = get_string(NULL); 972 name = get_string(NULL);
953 debug3("request %u: remove", id); 973 debug3("request %u: remove", id);
954 logit("remove name \"%s\"", name); 974 logit("remove name \"%s\"", name);
955 ret = unlink(name); 975 if (readonly)
956 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 976 status = SSH2_FX_PERMISSION_DENIED;
977 else {
978 ret = unlink(name);
979 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
980 }
957 send_status(id, status); 981 send_status(id, status);
958 xfree(name); 982 xfree(name);
959} 983}
@@ -973,8 +997,12 @@ process_mkdir(void)
973 a->perm & 07777 : 0777; 997 a->perm & 07777 : 0777;
974 debug3("request %u: mkdir", id); 998 debug3("request %u: mkdir", id);
975 logit("mkdir name \"%s\" mode 0%o", name, mode); 999 logit("mkdir name \"%s\" mode 0%o", name, mode);
976 ret = mkdir(name, mode); 1000 if (readonly)
977 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1001 status = SSH2_FX_PERMISSION_DENIED;
1002 else {
1003 ret = mkdir(name, mode);
1004 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1005 }
978 send_status(id, status); 1006 send_status(id, status);
979 xfree(name); 1007 xfree(name);
980} 1008}
@@ -990,8 +1018,12 @@ process_rmdir(void)
990 name = get_string(NULL); 1018 name = get_string(NULL);
991 debug3("request %u: rmdir", id); 1019 debug3("request %u: rmdir", id);
992 logit("rmdir name \"%s\"", name); 1020 logit("rmdir name \"%s\"", name);
993 ret = rmdir(name); 1021 if (readonly)
994 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1022 status = SSH2_FX_PERMISSION_DENIED;
1023 else {
1024 ret = rmdir(name);
1025 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1026 }
995 send_status(id, status); 1027 send_status(id, status);
996 xfree(name); 1028 xfree(name);
997} 1029}
@@ -1036,7 +1068,9 @@ process_rename(void)
1036 debug3("request %u: rename", id); 1068 debug3("request %u: rename", id);
1037 logit("rename old \"%s\" new \"%s\"", oldpath, newpath); 1069 logit("rename old \"%s\" new \"%s\"", oldpath, newpath);
1038 status = SSH2_FX_FAILURE; 1070 status = SSH2_FX_FAILURE;
1039 if (lstat(oldpath, &sb) == -1) 1071 if (readonly)
1072 status = SSH2_FX_PERMISSION_DENIED;
1073 else if (lstat(oldpath, &sb) == -1)
1040 status = errno_to_portable(errno); 1074 status = errno_to_portable(errno);
1041 else if (S_ISREG(sb.st_mode)) { 1075 else if (S_ISREG(sb.st_mode)) {
1042 /* Race-free rename of regular files */ 1076 /* Race-free rename of regular files */
@@ -1120,8 +1154,12 @@ process_symlink(void)
1120 debug3("request %u: symlink", id); 1154 debug3("request %u: symlink", id);
1121 logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); 1155 logit("symlink old \"%s\" new \"%s\"", oldpath, newpath);
1122 /* this will fail if 'newpath' exists */ 1156 /* this will fail if 'newpath' exists */
1123 ret = symlink(oldpath, newpath); 1157 if (readonly)
1124 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1158 status = SSH2_FX_PERMISSION_DENIED;
1159 else {
1160 ret = symlink(oldpath, newpath);
1161 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1162 }
1125 send_status(id, status); 1163 send_status(id, status);
1126 xfree(oldpath); 1164 xfree(oldpath);
1127 xfree(newpath); 1165 xfree(newpath);
@@ -1131,15 +1169,19 @@ static void
1131process_extended_posix_rename(u_int32_t id) 1169process_extended_posix_rename(u_int32_t id)
1132{ 1170{
1133 char *oldpath, *newpath; 1171 char *oldpath, *newpath;
1172 int ret, status;
1134 1173
1135 oldpath = get_string(NULL); 1174 oldpath = get_string(NULL);
1136 newpath = get_string(NULL); 1175 newpath = get_string(NULL);
1137 debug3("request %u: posix-rename", id); 1176 debug3("request %u: posix-rename", id);
1138 logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); 1177 logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath);
1139 if (rename(oldpath, newpath) == -1) 1178 if (readonly)
1140 send_status(id, errno_to_portable(errno)); 1179 status = SSH2_FX_PERMISSION_DENIED;
1141 else 1180 else {
1142 send_status(id, SSH2_FX_OK); 1181 ret = rename(oldpath, newpath);
1182 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1183 }
1184 send_status(id, status);
1143 xfree(oldpath); 1185 xfree(oldpath);
1144 xfree(newpath); 1186 xfree(newpath);
1145} 1187}
@@ -1322,7 +1364,7 @@ sftp_server_usage(void)
1322 extern char *__progname; 1364 extern char *__progname;
1323 1365
1324 fprintf(stderr, 1366 fprintf(stderr,
1325 "usage: %s [-eh] [-f log_facility] [-l log_level] [-u umask]\n", 1367 "usage: %s [-ehR] [-f log_facility] [-l log_level] [-u umask]\n",
1326 __progname); 1368 __progname);
1327 exit(1); 1369 exit(1);
1328} 1370}
@@ -1344,8 +1386,11 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw)
1344 __progname = ssh_get_progname(argv[0]); 1386 __progname = ssh_get_progname(argv[0]);
1345 log_init(__progname, log_level, log_facility, log_stderr); 1387 log_init(__progname, log_level, log_facility, log_stderr);
1346 1388
1347 while (!skipargs && (ch = getopt(argc, argv, "f:l:u:che")) != -1) { 1389 while (!skipargs && (ch = getopt(argc, argv, "f:l:u:cehR")) != -1) {
1348 switch (ch) { 1390 switch (ch) {
1391 case 'R':
1392 readonly = 1;
1393 break;
1349 case 'c': 1394 case 'c':
1350 /* 1395 /*
1351 * Ignore all arguments if we are invoked as a 1396 * Ignore all arguments if we are invoked as a