diff options
author | Damien Miller <djm@mindrot.org> | 2013-10-15 11:55:57 +1100 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2013-10-15 11:55:57 +1100 |
commit | 6eaeebf27d92f39a38c772aa3f20c2250af2dd29 (patch) | |
tree | 00c274ab9fbfaddd6c0a63881b1821c68b6c45da /sftp-server.c | |
parent | df62d71e64d29d1054e7a53d1a801075ef70335f (diff) |
- 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@
Diffstat (limited to 'sftp-server.c')
-rw-r--r-- | sftp-server.c | 403 |
1 files changed, 213 insertions, 190 deletions
diff --git a/sftp-server.c b/sftp-server.c index 285f21aaf..ca8335ec7 100644 --- a/sftp-server.c +++ b/sftp-server.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sftp-server.c,v 1.97 2013/05/17 00:13:14 djm Exp $ */ | 1 | /* $OpenBSD: sftp-server.c,v 1.98 2013/10/09 23:42:17 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 | * |
@@ -46,6 +46,7 @@ | |||
46 | #include "buffer.h" | 46 | #include "buffer.h" |
47 | #include "log.h" | 47 | #include "log.h" |
48 | #include "misc.h" | 48 | #include "misc.h" |
49 | #include "match.h" | ||
49 | #include "uidswap.h" | 50 | #include "uidswap.h" |
50 | 51 | ||
51 | #include "sftp.h" | 52 | #include "sftp.h" |
@@ -57,24 +58,29 @@ | |||
57 | #define get_string(lenp) buffer_get_string(&iqueue, lenp); | 58 | #define get_string(lenp) buffer_get_string(&iqueue, lenp); |
58 | 59 | ||
59 | /* Our verbosity */ | 60 | /* Our verbosity */ |
60 | LogLevel log_level = SYSLOG_LEVEL_ERROR; | 61 | static LogLevel log_level = SYSLOG_LEVEL_ERROR; |
61 | 62 | ||
62 | /* Our client */ | 63 | /* Our client */ |
63 | struct passwd *pw = NULL; | 64 | static struct passwd *pw = NULL; |
64 | char *client_addr = NULL; | 65 | static char *client_addr = NULL; |
65 | 66 | ||
66 | /* input and output queue */ | 67 | /* input and output queue */ |
67 | Buffer iqueue; | 68 | static Buffer iqueue; |
68 | Buffer oqueue; | 69 | static Buffer oqueue; |
69 | 70 | ||
70 | /* Version of client */ | 71 | /* Version of client */ |
71 | u_int version; | 72 | static u_int version; |
73 | |||
74 | /* SSH2_FXP_INIT received */ | ||
75 | static int init_done; | ||
72 | 76 | ||
73 | /* Disable writes */ | 77 | /* Disable writes */ |
74 | int readonly; | 78 | static int readonly; |
75 | 79 | ||
76 | /* portable attributes, etc. */ | 80 | /* Requests that are allowed/denied */ |
81 | static char *request_whitelist, *request_blacklist; | ||
77 | 82 | ||
83 | /* portable attributes, etc. */ | ||
78 | typedef struct Stat Stat; | 84 | typedef struct Stat Stat; |
79 | 85 | ||
80 | struct Stat { | 86 | struct Stat { |
@@ -83,6 +89,100 @@ struct Stat { | |||
83 | Attrib attrib; | 89 | Attrib attrib; |
84 | }; | 90 | }; |
85 | 91 | ||
92 | /* Packet handlers */ | ||
93 | static void process_open(u_int32_t id); | ||
94 | static void process_close(u_int32_t id); | ||
95 | static void process_read(u_int32_t id); | ||
96 | static void process_write(u_int32_t id); | ||
97 | static void process_stat(u_int32_t id); | ||
98 | static void process_lstat(u_int32_t id); | ||
99 | static void process_fstat(u_int32_t id); | ||
100 | static void process_setstat(u_int32_t id); | ||
101 | static void process_fsetstat(u_int32_t id); | ||
102 | static void process_opendir(u_int32_t id); | ||
103 | static void process_readdir(u_int32_t id); | ||
104 | static void process_remove(u_int32_t id); | ||
105 | static void process_mkdir(u_int32_t id); | ||
106 | static void process_rmdir(u_int32_t id); | ||
107 | static void process_realpath(u_int32_t id); | ||
108 | static void process_rename(u_int32_t id); | ||
109 | static void process_readlink(u_int32_t id); | ||
110 | static void process_symlink(u_int32_t id); | ||
111 | static void process_extended_posix_rename(u_int32_t id); | ||
112 | static void process_extended_statvfs(u_int32_t id); | ||
113 | static void process_extended_fstatvfs(u_int32_t id); | ||
114 | static void process_extended_hardlink(u_int32_t id); | ||
115 | static void process_extended(u_int32_t id); | ||
116 | |||
117 | struct sftp_handler { | ||
118 | const char *name; /* user-visible name for fine-grained perms */ | ||
119 | const char *ext_name; /* extended request name */ | ||
120 | u_int type; /* packet type, for non extended packets */ | ||
121 | void (*handler)(u_int32_t); | ||
122 | int does_write; /* if nonzero, banned for readonly mode */ | ||
123 | }; | ||
124 | |||
125 | struct sftp_handler handlers[] = { | ||
126 | /* NB. SSH2_FXP_OPEN does the readonly check in the handler itself */ | ||
127 | { "open", NULL, SSH2_FXP_OPEN, process_open, 0 }, | ||
128 | { "close", NULL, SSH2_FXP_CLOSE, process_close, 0 }, | ||
129 | { "read", NULL, SSH2_FXP_READ, process_read, 0 }, | ||
130 | { "write", NULL, SSH2_FXP_WRITE, process_write, 1 }, | ||
131 | { "lstat", NULL, SSH2_FXP_LSTAT, process_lstat, 0 }, | ||
132 | { "fstat", NULL, SSH2_FXP_FSTAT, process_fstat, 0 }, | ||
133 | { "setstat", NULL, SSH2_FXP_SETSTAT, process_setstat, 1 }, | ||
134 | { "fsetstat", NULL, SSH2_FXP_FSETSTAT, process_fsetstat, 1 }, | ||
135 | { "opendir", NULL, SSH2_FXP_OPENDIR, process_opendir, 0 }, | ||
136 | { "readdir", NULL, SSH2_FXP_READDIR, process_readdir, 0 }, | ||
137 | { "remove", NULL, SSH2_FXP_REMOVE, process_remove, 1 }, | ||
138 | { "mkdir", NULL, SSH2_FXP_MKDIR, process_mkdir, 1 }, | ||
139 | { "rmdir", NULL, SSH2_FXP_RMDIR, process_rmdir, 1 }, | ||
140 | { "realpath", NULL, SSH2_FXP_REALPATH, process_realpath, 0 }, | ||
141 | { "stat", NULL, SSH2_FXP_STAT, process_stat, 0 }, | ||
142 | { "rename", NULL, SSH2_FXP_RENAME, process_rename, 1 }, | ||
143 | { "readlink", NULL, SSH2_FXP_READLINK, process_readlink, 0 }, | ||
144 | { "symlink", NULL, SSH2_FXP_SYMLINK, process_symlink, 1 }, | ||
145 | { NULL, NULL, 0, NULL, 0 } | ||
146 | }; | ||
147 | |||
148 | /* SSH2_FXP_EXTENDED submessages */ | ||
149 | struct sftp_handler extended_handlers[] = { | ||
150 | { "posix-rename", "posix-rename@openssh.com", 0, | ||
151 | process_extended_posix_rename, 1 }, | ||
152 | { "statvfs", "statvfs@openssh.com", 0, process_extended_statvfs, 0 }, | ||
153 | { "fstatvfs", "fstatvfs@openssh.com", 0, process_extended_fstatvfs, 0 }, | ||
154 | { "hardlink", "hardlink@openssh.com", 0, process_extended_hardlink, 1 }, | ||
155 | { NULL, NULL, 0, NULL, 0 } | ||
156 | }; | ||
157 | |||
158 | static int | ||
159 | request_permitted(struct sftp_handler *h) | ||
160 | { | ||
161 | char *result; | ||
162 | |||
163 | if (readonly && h->does_write) { | ||
164 | verbose("Refusing %s request in read-only mode", h->name); | ||
165 | return 0; | ||
166 | } | ||
167 | if (request_blacklist != NULL && | ||
168 | ((result = match_list(h->name, request_blacklist, NULL))) != NULL) { | ||
169 | free(result); | ||
170 | verbose("Refusing blacklisted %s request", h->name); | ||
171 | return 0; | ||
172 | } | ||
173 | if (request_whitelist != NULL && | ||
174 | ((result = match_list(h->name, request_whitelist, NULL))) != NULL) { | ||
175 | free(result); | ||
176 | debug2("Permitting whitelisted %s request", h->name); | ||
177 | return 1; | ||
178 | } | ||
179 | if (request_whitelist != NULL) { | ||
180 | verbose("Refusing non-whitelisted %s request", h->name); | ||
181 | return 0; | ||
182 | } | ||
183 | return 1; | ||
184 | } | ||
185 | |||
86 | static int | 186 | static int |
87 | errno_to_portable(int unixerrno) | 187 | errno_to_portable(int unixerrno) |
88 | { | 188 | { |
@@ -543,14 +643,13 @@ process_init(void) | |||
543 | } | 643 | } |
544 | 644 | ||
545 | static void | 645 | static void |
546 | process_open(void) | 646 | process_open(u_int32_t id) |
547 | { | 647 | { |
548 | u_int32_t id, pflags; | 648 | u_int32_t pflags; |
549 | Attrib *a; | 649 | Attrib *a; |
550 | char *name; | 650 | char *name; |
551 | int handle, fd, flags, mode, status = SSH2_FX_FAILURE; | 651 | int handle, fd, flags, mode, status = SSH2_FX_FAILURE; |
552 | 652 | ||
553 | id = get_int(); | ||
554 | name = get_string(NULL); | 653 | name = get_string(NULL); |
555 | pflags = get_int(); /* portable flags */ | 654 | pflags = get_int(); /* portable flags */ |
556 | debug3("request %u: open flags %d", id, pflags); | 655 | debug3("request %u: open flags %d", id, pflags); |
@@ -560,9 +659,11 @@ process_open(void) | |||
560 | logit("open \"%s\" flags %s mode 0%o", | 659 | logit("open \"%s\" flags %s mode 0%o", |
561 | name, string_from_portable(pflags), mode); | 660 | name, string_from_portable(pflags), mode); |
562 | if (readonly && | 661 | if (readonly && |
563 | ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR)) | 662 | ((flags & O_ACCMODE) == O_WRONLY || |
564 | status = SSH2_FX_PERMISSION_DENIED; | 663 | (flags & O_ACCMODE) == O_RDWR)) { |
565 | else { | 664 | verbose("Refusing open request in read-only mode"); |
665 | status = SSH2_FX_PERMISSION_DENIED; | ||
666 | } else { | ||
566 | fd = open(name, flags, mode); | 667 | fd = open(name, flags, mode); |
567 | if (fd < 0) { | 668 | if (fd < 0) { |
568 | status = errno_to_portable(errno); | 669 | status = errno_to_portable(errno); |
@@ -582,12 +683,10 @@ process_open(void) | |||
582 | } | 683 | } |
583 | 684 | ||
584 | static void | 685 | static void |
585 | process_close(void) | 686 | process_close(u_int32_t id) |
586 | { | 687 | { |
587 | u_int32_t id; | ||
588 | int handle, ret, status = SSH2_FX_FAILURE; | 688 | int handle, ret, status = SSH2_FX_FAILURE; |
589 | 689 | ||
590 | id = get_int(); | ||
591 | handle = get_handle(); | 690 | handle = get_handle(); |
592 | debug3("request %u: close handle %u", id, handle); | 691 | debug3("request %u: close handle %u", id, handle); |
593 | handle_log_close(handle, NULL); | 692 | handle_log_close(handle, NULL); |
@@ -597,14 +696,13 @@ process_close(void) | |||
597 | } | 696 | } |
598 | 697 | ||
599 | static void | 698 | static void |
600 | process_read(void) | 699 | process_read(u_int32_t id) |
601 | { | 700 | { |
602 | char buf[64*1024]; | 701 | char buf[64*1024]; |
603 | u_int32_t id, len; | 702 | u_int32_t len; |
604 | int handle, fd, ret, status = SSH2_FX_FAILURE; | 703 | int handle, fd, ret, status = SSH2_FX_FAILURE; |
605 | u_int64_t off; | 704 | u_int64_t off; |
606 | 705 | ||
607 | id = get_int(); | ||
608 | handle = get_handle(); | 706 | handle = get_handle(); |
609 | off = get_int64(); | 707 | off = get_int64(); |
610 | len = get_int(); | 708 | len = get_int(); |
@@ -638,15 +736,13 @@ process_read(void) | |||
638 | } | 736 | } |
639 | 737 | ||
640 | static void | 738 | static void |
641 | process_write(void) | 739 | process_write(u_int32_t id) |
642 | { | 740 | { |
643 | u_int32_t id; | ||
644 | u_int64_t off; | 741 | u_int64_t off; |
645 | u_int len; | 742 | u_int len; |
646 | int handle, fd, ret, status; | 743 | int handle, fd, ret, status; |
647 | char *data; | 744 | char *data; |
648 | 745 | ||
649 | id = get_int(); | ||
650 | handle = get_handle(); | 746 | handle = get_handle(); |
651 | off = get_int64(); | 747 | off = get_int64(); |
652 | data = get_string(&len); | 748 | data = get_string(&len); |
@@ -657,8 +753,6 @@ process_write(void) | |||
657 | 753 | ||
658 | if (fd < 0) | 754 | if (fd < 0) |
659 | status = SSH2_FX_FAILURE; | 755 | status = SSH2_FX_FAILURE; |
660 | else if (readonly) | ||
661 | status = SSH2_FX_PERMISSION_DENIED; | ||
662 | else { | 756 | else { |
663 | if (lseek(fd, off, SEEK_SET) < 0) { | 757 | if (lseek(fd, off, SEEK_SET) < 0) { |
664 | status = errno_to_portable(errno); | 758 | status = errno_to_portable(errno); |
@@ -683,15 +777,13 @@ process_write(void) | |||
683 | } | 777 | } |
684 | 778 | ||
685 | static void | 779 | static void |
686 | process_do_stat(int do_lstat) | 780 | process_do_stat(u_int32_t id, int do_lstat) |
687 | { | 781 | { |
688 | Attrib a; | 782 | Attrib a; |
689 | struct stat st; | 783 | struct stat st; |
690 | u_int32_t id; | ||
691 | char *name; | 784 | char *name; |
692 | int ret, status = SSH2_FX_FAILURE; | 785 | int ret, status = SSH2_FX_FAILURE; |
693 | 786 | ||
694 | id = get_int(); | ||
695 | name = get_string(NULL); | 787 | name = get_string(NULL); |
696 | debug3("request %u: %sstat", id, do_lstat ? "l" : ""); | 788 | debug3("request %u: %sstat", id, do_lstat ? "l" : ""); |
697 | verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); | 789 | verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); |
@@ -709,26 +801,24 @@ process_do_stat(int do_lstat) | |||
709 | } | 801 | } |
710 | 802 | ||
711 | static void | 803 | static void |
712 | process_stat(void) | 804 | process_stat(u_int32_t id) |
713 | { | 805 | { |
714 | process_do_stat(0); | 806 | process_do_stat(id, 0); |
715 | } | 807 | } |
716 | 808 | ||
717 | static void | 809 | static void |
718 | process_lstat(void) | 810 | process_lstat(u_int32_t id) |
719 | { | 811 | { |
720 | process_do_stat(1); | 812 | process_do_stat(id, 1); |
721 | } | 813 | } |
722 | 814 | ||
723 | static void | 815 | static void |
724 | process_fstat(void) | 816 | process_fstat(u_int32_t id) |
725 | { | 817 | { |
726 | Attrib a; | 818 | Attrib a; |
727 | struct stat st; | 819 | struct stat st; |
728 | u_int32_t id; | ||
729 | int fd, ret, handle, status = SSH2_FX_FAILURE; | 820 | int fd, ret, handle, status = SSH2_FX_FAILURE; |
730 | 821 | ||
731 | id = get_int(); | ||
732 | handle = get_handle(); | 822 | handle = get_handle(); |
733 | debug("request %u: fstat \"%s\" (handle %u)", | 823 | debug("request %u: fstat \"%s\" (handle %u)", |
734 | id, handle_to_name(handle), handle); | 824 | id, handle_to_name(handle), handle); |
@@ -760,21 +850,15 @@ attrib_to_tv(const Attrib *a) | |||
760 | } | 850 | } |
761 | 851 | ||
762 | static void | 852 | static void |
763 | process_setstat(void) | 853 | process_setstat(u_int32_t id) |
764 | { | 854 | { |
765 | Attrib *a; | 855 | Attrib *a; |
766 | u_int32_t id; | ||
767 | char *name; | 856 | char *name; |
768 | int status = SSH2_FX_OK, ret; | 857 | int status = SSH2_FX_OK, ret; |
769 | 858 | ||
770 | id = get_int(); | ||
771 | name = get_string(NULL); | 859 | name = get_string(NULL); |
772 | a = get_attrib(); | 860 | a = get_attrib(); |
773 | debug("request %u: setstat name \"%s\"", id, name); | 861 | debug("request %u: setstat name \"%s\"", id, name); |
774 | if (readonly) { | ||
775 | status = SSH2_FX_PERMISSION_DENIED; | ||
776 | a->flags = 0; | ||
777 | } | ||
778 | if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { | 862 | if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { |
779 | logit("set \"%s\" size %llu", | 863 | logit("set \"%s\" size %llu", |
780 | name, (unsigned long long)a->size); | 864 | name, (unsigned long long)a->size); |
@@ -811,22 +895,18 @@ process_setstat(void) | |||
811 | } | 895 | } |
812 | 896 | ||
813 | static void | 897 | static void |
814 | process_fsetstat(void) | 898 | process_fsetstat(u_int32_t id) |
815 | { | 899 | { |
816 | Attrib *a; | 900 | Attrib *a; |
817 | u_int32_t id; | ||
818 | int handle, fd, ret; | 901 | int handle, fd, ret; |
819 | int status = SSH2_FX_OK; | 902 | int status = SSH2_FX_OK; |
820 | 903 | ||
821 | id = get_int(); | ||
822 | handle = get_handle(); | 904 | handle = get_handle(); |
823 | a = get_attrib(); | 905 | a = get_attrib(); |
824 | debug("request %u: fsetstat handle %d", id, handle); | 906 | debug("request %u: fsetstat handle %d", id, handle); |
825 | fd = handle_to_fd(handle); | 907 | fd = handle_to_fd(handle); |
826 | if (fd < 0) | 908 | if (fd < 0) |
827 | status = SSH2_FX_FAILURE; | 909 | status = SSH2_FX_FAILURE; |
828 | else if (readonly) | ||
829 | status = SSH2_FX_PERMISSION_DENIED; | ||
830 | else { | 910 | else { |
831 | char *name = handle_to_name(handle); | 911 | char *name = handle_to_name(handle); |
832 | 912 | ||
@@ -878,14 +958,12 @@ process_fsetstat(void) | |||
878 | } | 958 | } |
879 | 959 | ||
880 | static void | 960 | static void |
881 | process_opendir(void) | 961 | process_opendir(u_int32_t id) |
882 | { | 962 | { |
883 | DIR *dirp = NULL; | 963 | DIR *dirp = NULL; |
884 | char *path; | 964 | char *path; |
885 | int handle, status = SSH2_FX_FAILURE; | 965 | int handle, status = SSH2_FX_FAILURE; |
886 | u_int32_t id; | ||
887 | 966 | ||
888 | id = get_int(); | ||
889 | path = get_string(NULL); | 967 | path = get_string(NULL); |
890 | debug3("request %u: opendir", id); | 968 | debug3("request %u: opendir", id); |
891 | logit("opendir \"%s\"", path); | 969 | logit("opendir \"%s\"", path); |
@@ -908,15 +986,13 @@ process_opendir(void) | |||
908 | } | 986 | } |
909 | 987 | ||
910 | static void | 988 | static void |
911 | process_readdir(void) | 989 | process_readdir(u_int32_t id) |
912 | { | 990 | { |
913 | DIR *dirp; | 991 | DIR *dirp; |
914 | struct dirent *dp; | 992 | struct dirent *dp; |
915 | char *path; | 993 | char *path; |
916 | int handle; | 994 | int handle; |
917 | u_int32_t id; | ||
918 | 995 | ||
919 | id = get_int(); | ||
920 | handle = get_handle(); | 996 | handle = get_handle(); |
921 | debug("request %u: readdir \"%s\" (handle %d)", id, | 997 | debug("request %u: readdir \"%s\" (handle %d)", id, |
922 | handle_to_name(handle), handle); | 998 | handle_to_name(handle), handle); |
@@ -964,81 +1040,61 @@ process_readdir(void) | |||
964 | } | 1040 | } |
965 | 1041 | ||
966 | static void | 1042 | static void |
967 | process_remove(void) | 1043 | process_remove(u_int32_t id) |
968 | { | 1044 | { |
969 | char *name; | 1045 | char *name; |
970 | u_int32_t id; | ||
971 | int status = SSH2_FX_FAILURE; | 1046 | int status = SSH2_FX_FAILURE; |
972 | int ret; | 1047 | int ret; |
973 | 1048 | ||
974 | id = get_int(); | ||
975 | name = get_string(NULL); | 1049 | name = get_string(NULL); |
976 | debug3("request %u: remove", id); | 1050 | debug3("request %u: remove", id); |
977 | logit("remove name \"%s\"", name); | 1051 | logit("remove name \"%s\"", name); |
978 | if (readonly) | 1052 | ret = unlink(name); |
979 | status = SSH2_FX_PERMISSION_DENIED; | 1053 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
980 | else { | ||
981 | ret = unlink(name); | ||
982 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; | ||
983 | } | ||
984 | send_status(id, status); | 1054 | send_status(id, status); |
985 | free(name); | 1055 | free(name); |
986 | } | 1056 | } |
987 | 1057 | ||
988 | static void | 1058 | static void |
989 | process_mkdir(void) | 1059 | process_mkdir(u_int32_t id) |
990 | { | 1060 | { |
991 | Attrib *a; | 1061 | Attrib *a; |
992 | u_int32_t id; | ||
993 | char *name; | 1062 | char *name; |
994 | int ret, mode, status = SSH2_FX_FAILURE; | 1063 | int ret, mode, status = SSH2_FX_FAILURE; |
995 | 1064 | ||
996 | id = get_int(); | ||
997 | name = get_string(NULL); | 1065 | name = get_string(NULL); |
998 | a = get_attrib(); | 1066 | a = get_attrib(); |
999 | mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? | 1067 | mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? |
1000 | a->perm & 07777 : 0777; | 1068 | a->perm & 07777 : 0777; |
1001 | debug3("request %u: mkdir", id); | 1069 | debug3("request %u: mkdir", id); |
1002 | logit("mkdir name \"%s\" mode 0%o", name, mode); | 1070 | logit("mkdir name \"%s\" mode 0%o", name, mode); |
1003 | if (readonly) | 1071 | ret = mkdir(name, mode); |
1004 | status = SSH2_FX_PERMISSION_DENIED; | 1072 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
1005 | else { | ||
1006 | ret = mkdir(name, mode); | ||
1007 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; | ||
1008 | } | ||
1009 | send_status(id, status); | 1073 | send_status(id, status); |
1010 | free(name); | 1074 | free(name); |
1011 | } | 1075 | } |
1012 | 1076 | ||
1013 | static void | 1077 | static void |
1014 | process_rmdir(void) | 1078 | process_rmdir(u_int32_t id) |
1015 | { | 1079 | { |
1016 | u_int32_t id; | ||
1017 | char *name; | 1080 | char *name; |
1018 | int ret, status; | 1081 | int ret, status; |
1019 | 1082 | ||
1020 | id = get_int(); | ||
1021 | name = get_string(NULL); | 1083 | name = get_string(NULL); |
1022 | debug3("request %u: rmdir", id); | 1084 | debug3("request %u: rmdir", id); |
1023 | logit("rmdir name \"%s\"", name); | 1085 | logit("rmdir name \"%s\"", name); |
1024 | if (readonly) | 1086 | ret = rmdir(name); |
1025 | status = SSH2_FX_PERMISSION_DENIED; | 1087 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
1026 | else { | ||
1027 | ret = rmdir(name); | ||
1028 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; | ||
1029 | } | ||
1030 | send_status(id, status); | 1088 | send_status(id, status); |
1031 | free(name); | 1089 | free(name); |
1032 | } | 1090 | } |
1033 | 1091 | ||
1034 | static void | 1092 | static void |
1035 | process_realpath(void) | 1093 | process_realpath(u_int32_t id) |
1036 | { | 1094 | { |
1037 | char resolvedname[MAXPATHLEN]; | 1095 | char resolvedname[MAXPATHLEN]; |
1038 | u_int32_t id; | ||
1039 | char *path; | 1096 | char *path; |
1040 | 1097 | ||
1041 | id = get_int(); | ||
1042 | path = get_string(NULL); | 1098 | path = get_string(NULL); |
1043 | if (path[0] == '\0') { | 1099 | if (path[0] == '\0') { |
1044 | free(path); | 1100 | free(path); |
@@ -1058,22 +1114,18 @@ process_realpath(void) | |||
1058 | } | 1114 | } |
1059 | 1115 | ||
1060 | static void | 1116 | static void |
1061 | process_rename(void) | 1117 | process_rename(u_int32_t id) |
1062 | { | 1118 | { |
1063 | u_int32_t id; | ||
1064 | char *oldpath, *newpath; | 1119 | char *oldpath, *newpath; |
1065 | int status; | 1120 | int status; |
1066 | struct stat sb; | 1121 | struct stat sb; |
1067 | 1122 | ||
1068 | id = get_int(); | ||
1069 | oldpath = get_string(NULL); | 1123 | oldpath = get_string(NULL); |
1070 | newpath = get_string(NULL); | 1124 | newpath = get_string(NULL); |
1071 | debug3("request %u: rename", id); | 1125 | debug3("request %u: rename", id); |
1072 | logit("rename old \"%s\" new \"%s\"", oldpath, newpath); | 1126 | logit("rename old \"%s\" new \"%s\"", oldpath, newpath); |
1073 | status = SSH2_FX_FAILURE; | 1127 | status = SSH2_FX_FAILURE; |
1074 | if (readonly) | 1128 | if (lstat(oldpath, &sb) == -1) |
1075 | status = SSH2_FX_PERMISSION_DENIED; | ||
1076 | else if (lstat(oldpath, &sb) == -1) | ||
1077 | status = errno_to_portable(errno); | 1129 | status = errno_to_portable(errno); |
1078 | else if (S_ISREG(sb.st_mode)) { | 1130 | else if (S_ISREG(sb.st_mode)) { |
1079 | /* Race-free rename of regular files */ | 1131 | /* Race-free rename of regular files */ |
@@ -1120,14 +1172,12 @@ process_rename(void) | |||
1120 | } | 1172 | } |
1121 | 1173 | ||
1122 | static void | 1174 | static void |
1123 | process_readlink(void) | 1175 | process_readlink(u_int32_t id) |
1124 | { | 1176 | { |
1125 | u_int32_t id; | ||
1126 | int len; | 1177 | int len; |
1127 | char buf[MAXPATHLEN]; | 1178 | char buf[MAXPATHLEN]; |
1128 | char *path; | 1179 | char *path; |
1129 | 1180 | ||
1130 | id = get_int(); | ||
1131 | path = get_string(NULL); | 1181 | path = get_string(NULL); |
1132 | debug3("request %u: readlink", id); | 1182 | debug3("request %u: readlink", id); |
1133 | verbose("readlink \"%s\"", path); | 1183 | verbose("readlink \"%s\"", path); |
@@ -1145,24 +1195,18 @@ process_readlink(void) | |||
1145 | } | 1195 | } |
1146 | 1196 | ||
1147 | static void | 1197 | static void |
1148 | process_symlink(void) | 1198 | process_symlink(u_int32_t id) |
1149 | { | 1199 | { |
1150 | u_int32_t id; | ||
1151 | char *oldpath, *newpath; | 1200 | char *oldpath, *newpath; |
1152 | int ret, status; | 1201 | int ret, status; |
1153 | 1202 | ||
1154 | id = get_int(); | ||
1155 | oldpath = get_string(NULL); | 1203 | oldpath = get_string(NULL); |
1156 | newpath = get_string(NULL); | 1204 | newpath = get_string(NULL); |
1157 | debug3("request %u: symlink", id); | 1205 | debug3("request %u: symlink", id); |
1158 | logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); | 1206 | logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); |
1159 | /* this will fail if 'newpath' exists */ | 1207 | /* this will fail if 'newpath' exists */ |
1160 | if (readonly) | 1208 | ret = symlink(oldpath, newpath); |
1161 | status = SSH2_FX_PERMISSION_DENIED; | 1209 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
1162 | else { | ||
1163 | ret = symlink(oldpath, newpath); | ||
1164 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; | ||
1165 | } | ||
1166 | send_status(id, status); | 1210 | send_status(id, status); |
1167 | free(oldpath); | 1211 | free(oldpath); |
1168 | free(newpath); | 1212 | free(newpath); |
@@ -1178,12 +1222,8 @@ process_extended_posix_rename(u_int32_t id) | |||
1178 | newpath = get_string(NULL); | 1222 | newpath = get_string(NULL); |
1179 | debug3("request %u: posix-rename", id); | 1223 | debug3("request %u: posix-rename", id); |
1180 | logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); | 1224 | logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); |
1181 | if (readonly) | 1225 | ret = rename(oldpath, newpath); |
1182 | status = SSH2_FX_PERMISSION_DENIED; | 1226 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
1183 | else { | ||
1184 | ret = rename(oldpath, newpath); | ||
1185 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; | ||
1186 | } | ||
1187 | send_status(id, status); | 1227 | send_status(id, status); |
1188 | free(oldpath); | 1228 | free(oldpath); |
1189 | free(newpath); | 1229 | free(newpath); |
@@ -1235,35 +1275,33 @@ process_extended_hardlink(u_int32_t id) | |||
1235 | newpath = get_string(NULL); | 1275 | newpath = get_string(NULL); |
1236 | debug3("request %u: hardlink", id); | 1276 | debug3("request %u: hardlink", id); |
1237 | logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath); | 1277 | logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath); |
1238 | if (readonly) | 1278 | ret = link(oldpath, newpath); |
1239 | status = SSH2_FX_PERMISSION_DENIED; | 1279 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
1240 | else { | ||
1241 | ret = link(oldpath, newpath); | ||
1242 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; | ||
1243 | } | ||
1244 | send_status(id, status); | 1280 | send_status(id, status); |
1245 | free(oldpath); | 1281 | free(oldpath); |
1246 | free(newpath); | 1282 | free(newpath); |
1247 | } | 1283 | } |
1248 | 1284 | ||
1249 | static void | 1285 | static void |
1250 | process_extended(void) | 1286 | process_extended(u_int32_t id) |
1251 | { | 1287 | { |
1252 | u_int32_t id; | ||
1253 | char *request; | 1288 | char *request; |
1289 | u_int i; | ||
1254 | 1290 | ||
1255 | id = get_int(); | ||
1256 | request = get_string(NULL); | 1291 | request = get_string(NULL); |
1257 | if (strcmp(request, "posix-rename@openssh.com") == 0) | 1292 | for (i = 0; extended_handlers[i].handler != NULL; i++) { |
1258 | process_extended_posix_rename(id); | 1293 | if (strcmp(request, extended_handlers[i].ext_name) == 0) { |
1259 | else if (strcmp(request, "statvfs@openssh.com") == 0) | 1294 | if (!request_permitted(&extended_handlers[i])) |
1260 | process_extended_statvfs(id); | 1295 | send_status(id, SSH2_FX_PERMISSION_DENIED); |
1261 | else if (strcmp(request, "fstatvfs@openssh.com") == 0) | 1296 | else |
1262 | process_extended_fstatvfs(id); | 1297 | extended_handlers[i].handler(id); |
1263 | else if (strcmp(request, "hardlink@openssh.com") == 0) | 1298 | break; |
1264 | process_extended_hardlink(id); | 1299 | } |
1265 | else | 1300 | } |
1301 | if (extended_handlers[i].handler == NULL) { | ||
1302 | error("Unknown extended request \"%.100s\"", request); | ||
1266 | send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ | 1303 | send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ |
1304 | } | ||
1267 | free(request); | 1305 | free(request); |
1268 | } | 1306 | } |
1269 | 1307 | ||
@@ -1272,11 +1310,9 @@ process_extended(void) | |||
1272 | static void | 1310 | static void |
1273 | process(void) | 1311 | process(void) |
1274 | { | 1312 | { |
1275 | u_int msg_len; | 1313 | u_int msg_len, buf_len, consumed, type, i; |
1276 | u_int buf_len; | ||
1277 | u_int consumed; | ||
1278 | u_int type; | ||
1279 | u_char *cp; | 1314 | u_char *cp; |
1315 | u_int32_t id; | ||
1280 | 1316 | ||
1281 | buf_len = buffer_len(&iqueue); | 1317 | buf_len = buffer_len(&iqueue); |
1282 | if (buf_len < 5) | 1318 | if (buf_len < 5) |
@@ -1293,70 +1329,35 @@ process(void) | |||
1293 | buffer_consume(&iqueue, 4); | 1329 | buffer_consume(&iqueue, 4); |
1294 | buf_len -= 4; | 1330 | buf_len -= 4; |
1295 | type = buffer_get_char(&iqueue); | 1331 | type = buffer_get_char(&iqueue); |
1332 | |||
1296 | switch (type) { | 1333 | switch (type) { |
1297 | case SSH2_FXP_INIT: | 1334 | case SSH2_FXP_INIT: |
1298 | process_init(); | 1335 | process_init(); |
1299 | break; | 1336 | init_done = 1; |
1300 | case SSH2_FXP_OPEN: | ||
1301 | process_open(); | ||
1302 | break; | ||
1303 | case SSH2_FXP_CLOSE: | ||
1304 | process_close(); | ||
1305 | break; | ||
1306 | case SSH2_FXP_READ: | ||
1307 | process_read(); | ||
1308 | break; | ||
1309 | case SSH2_FXP_WRITE: | ||
1310 | process_write(); | ||
1311 | break; | ||
1312 | case SSH2_FXP_LSTAT: | ||
1313 | process_lstat(); | ||
1314 | break; | ||
1315 | case SSH2_FXP_FSTAT: | ||
1316 | process_fstat(); | ||
1317 | break; | ||
1318 | case SSH2_FXP_SETSTAT: | ||
1319 | process_setstat(); | ||
1320 | break; | ||
1321 | case SSH2_FXP_FSETSTAT: | ||
1322 | process_fsetstat(); | ||
1323 | break; | ||
1324 | case SSH2_FXP_OPENDIR: | ||
1325 | process_opendir(); | ||
1326 | break; | ||
1327 | case SSH2_FXP_READDIR: | ||
1328 | process_readdir(); | ||
1329 | break; | ||
1330 | case SSH2_FXP_REMOVE: | ||
1331 | process_remove(); | ||
1332 | break; | ||
1333 | case SSH2_FXP_MKDIR: | ||
1334 | process_mkdir(); | ||
1335 | break; | ||
1336 | case SSH2_FXP_RMDIR: | ||
1337 | process_rmdir(); | ||
1338 | break; | ||
1339 | case SSH2_FXP_REALPATH: | ||
1340 | process_realpath(); | ||
1341 | break; | ||
1342 | case SSH2_FXP_STAT: | ||
1343 | process_stat(); | ||
1344 | break; | ||
1345 | case SSH2_FXP_RENAME: | ||
1346 | process_rename(); | ||
1347 | break; | ||
1348 | case SSH2_FXP_READLINK: | ||
1349 | process_readlink(); | ||
1350 | break; | ||
1351 | case SSH2_FXP_SYMLINK: | ||
1352 | process_symlink(); | ||
1353 | break; | 1337 | break; |
1354 | case SSH2_FXP_EXTENDED: | 1338 | case SSH2_FXP_EXTENDED: |
1355 | process_extended(); | 1339 | if (!init_done) |
1340 | fatal("Received extended request before init"); | ||
1341 | id = get_int(); | ||
1342 | process_extended(id); | ||
1356 | break; | 1343 | break; |
1357 | default: | 1344 | default: |
1358 | error("Unknown message %d", type); | 1345 | if (!init_done) |
1359 | break; | 1346 | fatal("Received %u request before init", type); |
1347 | id = get_int(); | ||
1348 | for (i = 0; handlers[i].handler != NULL; i++) { | ||
1349 | if (type == handlers[i].type) { | ||
1350 | if (!request_permitted(&handlers[i])) { | ||
1351 | send_status(id, | ||
1352 | SSH2_FX_PERMISSION_DENIED); | ||
1353 | } else { | ||
1354 | handlers[i].handler(id); | ||
1355 | } | ||
1356 | break; | ||
1357 | } | ||
1358 | } | ||
1359 | if (handlers[i].handler == NULL) | ||
1360 | error("Unknown message %u", type); | ||
1360 | } | 1361 | } |
1361 | /* discard the remaining bytes from the current packet */ | 1362 | /* discard the remaining bytes from the current packet */ |
1362 | if (buf_len < buffer_len(&iqueue)) { | 1363 | if (buf_len < buffer_len(&iqueue)) { |
@@ -1365,7 +1366,7 @@ process(void) | |||
1365 | } | 1366 | } |
1366 | consumed = buf_len - buffer_len(&iqueue); | 1367 | consumed = buf_len - buffer_len(&iqueue); |
1367 | if (msg_len < consumed) { | 1368 | if (msg_len < consumed) { |
1368 | error("msg_len %d < consumed %d", msg_len, consumed); | 1369 | error("msg_len %u < consumed %u", msg_len, consumed); |
1369 | sftp_server_cleanup_exit(255); | 1370 | sftp_server_cleanup_exit(255); |
1370 | } | 1371 | } |
1371 | if (msg_len > consumed) | 1372 | if (msg_len > consumed) |
@@ -1400,7 +1401,7 @@ int | |||
1400 | sftp_server_main(int argc, char **argv, struct passwd *user_pw) | 1401 | sftp_server_main(int argc, char **argv, struct passwd *user_pw) |
1401 | { | 1402 | { |
1402 | fd_set *rset, *wset; | 1403 | fd_set *rset, *wset; |
1403 | int in, out, max, ch, skipargs = 0, log_stderr = 0; | 1404 | int i, in, out, max, ch, skipargs = 0, log_stderr = 0; |
1404 | ssize_t len, olen, set_size; | 1405 | ssize_t len, olen, set_size; |
1405 | SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; | 1406 | SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; |
1406 | char *cp, *homedir = NULL, buf[4*4096]; | 1407 | char *cp, *homedir = NULL, buf[4*4096]; |
@@ -1414,8 +1415,20 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) | |||
1414 | 1415 | ||
1415 | pw = pwcopy(user_pw); | 1416 | pw = pwcopy(user_pw); |
1416 | 1417 | ||
1417 | while (!skipargs && (ch = getopt(argc, argv, "d:f:l:u:cehR")) != -1) { | 1418 | while (!skipargs && (ch = getopt(argc, argv, |
1419 | "d:f:l:P:p:Q:u:cehR")) != -1) { | ||
1418 | switch (ch) { | 1420 | switch (ch) { |
1421 | case 'Q': | ||
1422 | if (strcasecmp(optarg, "requests") != 0) { | ||
1423 | fprintf(stderr, "Invalid query type\n"); | ||
1424 | exit(1); | ||
1425 | } | ||
1426 | for (i = 0; handlers[i].handler != NULL; i++) | ||
1427 | printf("%s\n", handlers[i].name); | ||
1428 | for (i = 0; extended_handlers[i].handler != NULL; i++) | ||
1429 | printf("%s\n", extended_handlers[i].name); | ||
1430 | exit(0); | ||
1431 | break; | ||
1419 | case 'R': | 1432 | case 'R': |
1420 | readonly = 1; | 1433 | readonly = 1; |
1421 | break; | 1434 | break; |
@@ -1445,6 +1458,16 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) | |||
1445 | "u", user_pw->pw_name, (char *)NULL); | 1458 | "u", user_pw->pw_name, (char *)NULL); |
1446 | free(cp); | 1459 | free(cp); |
1447 | break; | 1460 | break; |
1461 | case 'p': | ||
1462 | if (request_whitelist != NULL) | ||
1463 | fatal("Permitted requests already set"); | ||
1464 | request_whitelist = xstrdup(optarg); | ||
1465 | break; | ||
1466 | case 'P': | ||
1467 | if (request_blacklist != NULL) | ||
1468 | fatal("Refused requests already set"); | ||
1469 | request_blacklist = xstrdup(optarg); | ||
1470 | break; | ||
1448 | case 'u': | 1471 | case 'u': |
1449 | errno = 0; | 1472 | errno = 0; |
1450 | mask = strtol(optarg, &cp, 8); | 1473 | mask = strtol(optarg, &cp, 8); |