diff options
Diffstat (limited to 'sftp-server.c')
-rw-r--r-- | sftp-server.c | 119 |
1 files changed, 87 insertions, 32 deletions
diff --git a/sftp-server.c b/sftp-server.c index d984e6049..a98ac2b6d 100644 --- a/sftp-server.c +++ b/sftp-server.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sftp-server.c,v 1.85 2009/04/14 16:33:42 stevesk Exp $ */ | 1 | /* $OpenBSD: sftp-server.c,v 1.91 2010/01/13 01:40:16 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 */ |
71 | int version; | 71 | int version; |
72 | 72 | ||
73 | /* Disable writes */ | ||
74 | int readonly; | ||
75 | |||
73 | /* portable attributes, etc. */ | 76 | /* portable attributes, etc. */ |
74 | 77 | ||
75 | typedef struct Stat Stat; | 78 | typedef 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) { |
@@ -920,7 +940,7 @@ process_readdir(void) | |||
920 | continue; | 940 | continue; |
921 | stat_to_attrib(&st, &(stats[count].attrib)); | 941 | stat_to_attrib(&st, &(stats[count].attrib)); |
922 | stats[count].name = xstrdup(dp->d_name); | 942 | stats[count].name = xstrdup(dp->d_name); |
923 | stats[count].long_name = ls_file(dp->d_name, &st, 0); | 943 | stats[count].long_name = ls_file(dp->d_name, &st, 0, 0); |
924 | count++; | 944 | count++; |
925 | /* send up to 100 entries in one message */ | 945 | /* send up to 100 entries in one message */ |
926 | /* XXX check packet size instead */ | 946 | /* XXX check packet size instead */ |
@@ -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 | |||
1131 | process_extended_posix_rename(u_int32_t id) | 1169 | process_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,8 @@ sftp_server_usage(void) | |||
1322 | extern char *__progname; | 1364 | extern char *__progname; |
1323 | 1365 | ||
1324 | fprintf(stderr, | 1366 | fprintf(stderr, |
1325 | "usage: %s [-he] [-l log_level] [-f log_facility]\n", __progname); | 1367 | "usage: %s [-ehR] [-f log_facility] [-l log_level] [-u umask]\n", |
1368 | __progname); | ||
1326 | exit(1); | 1369 | exit(1); |
1327 | } | 1370 | } |
1328 | 1371 | ||
@@ -1334,6 +1377,8 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) | |||
1334 | ssize_t len, olen, set_size; | 1377 | ssize_t len, olen, set_size; |
1335 | SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; | 1378 | SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; |
1336 | char *cp, buf[4*4096]; | 1379 | char *cp, buf[4*4096]; |
1380 | const char *errmsg; | ||
1381 | mode_t mask; | ||
1337 | 1382 | ||
1338 | extern char *optarg; | 1383 | extern char *optarg; |
1339 | extern char *__progname; | 1384 | extern char *__progname; |
@@ -1341,8 +1386,11 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) | |||
1341 | __progname = ssh_get_progname(argv[0]); | 1386 | __progname = ssh_get_progname(argv[0]); |
1342 | log_init(__progname, log_level, log_facility, log_stderr); | 1387 | log_init(__progname, log_level, log_facility, log_stderr); |
1343 | 1388 | ||
1344 | while (!skipargs && (ch = getopt(argc, argv, "f:l:che")) != -1) { | 1389 | while (!skipargs && (ch = getopt(argc, argv, "f:l:u:cehR")) != -1) { |
1345 | switch (ch) { | 1390 | switch (ch) { |
1391 | case 'R': | ||
1392 | readonly = 1; | ||
1393 | break; | ||
1346 | case 'c': | 1394 | case 'c': |
1347 | /* | 1395 | /* |
1348 | * Ignore all arguments if we are invoked as a | 1396 | * Ignore all arguments if we are invoked as a |
@@ -1363,6 +1411,13 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) | |||
1363 | if (log_facility == SYSLOG_FACILITY_NOT_SET) | 1411 | if (log_facility == SYSLOG_FACILITY_NOT_SET) |
1364 | error("Invalid log facility \"%s\"", optarg); | 1412 | error("Invalid log facility \"%s\"", optarg); |
1365 | break; | 1413 | break; |
1414 | case 'u': | ||
1415 | mask = (mode_t)strtonum(optarg, 0, 0777, &errmsg); | ||
1416 | if (errmsg != NULL) | ||
1417 | fatal("Invalid umask \"%s\": %s", | ||
1418 | optarg, errmsg); | ||
1419 | (void)umask(mask); | ||
1420 | break; | ||
1366 | case 'h': | 1421 | case 'h': |
1367 | default: | 1422 | default: |
1368 | sftp_server_usage(); | 1423 | sftp_server_usage(); |
@@ -1387,8 +1442,8 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) | |||
1387 | logit("session opened for local user %s from [%s]", | 1442 | logit("session opened for local user %s from [%s]", |
1388 | pw->pw_name, client_addr); | 1443 | pw->pw_name, client_addr); |
1389 | 1444 | ||
1390 | in = dup(STDIN_FILENO); | 1445 | in = STDIN_FILENO; |
1391 | out = dup(STDOUT_FILENO); | 1446 | out = STDOUT_FILENO; |
1392 | 1447 | ||
1393 | #ifdef HAVE_CYGWIN | 1448 | #ifdef HAVE_CYGWIN |
1394 | setmode(in, O_BINARY); | 1449 | setmode(in, O_BINARY); |