diff options
Diffstat (limited to 'sftp-server.c')
-rw-r--r-- | sftp-server.c | 458 |
1 files changed, 260 insertions, 198 deletions
diff --git a/sftp-server.c b/sftp-server.c index 285f21aaf..b8eb59c36 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.103 2014/01/17 06:23:24 dtucker 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,102 @@ 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_fsync(u_int32_t id); | ||
116 | static void process_extended(u_int32_t id); | ||
117 | |||
118 | struct sftp_handler { | ||
119 | const char *name; /* user-visible name for fine-grained perms */ | ||
120 | const char *ext_name; /* extended request name */ | ||
121 | u_int type; /* packet type, for non extended packets */ | ||
122 | void (*handler)(u_int32_t); | ||
123 | int does_write; /* if nonzero, banned for readonly mode */ | ||
124 | }; | ||
125 | |||
126 | struct sftp_handler handlers[] = { | ||
127 | /* NB. SSH2_FXP_OPEN does the readonly check in the handler itself */ | ||
128 | { "open", NULL, SSH2_FXP_OPEN, process_open, 0 }, | ||
129 | { "close", NULL, SSH2_FXP_CLOSE, process_close, 0 }, | ||
130 | { "read", NULL, SSH2_FXP_READ, process_read, 0 }, | ||
131 | { "write", NULL, SSH2_FXP_WRITE, process_write, 1 }, | ||
132 | { "lstat", NULL, SSH2_FXP_LSTAT, process_lstat, 0 }, | ||
133 | { "fstat", NULL, SSH2_FXP_FSTAT, process_fstat, 0 }, | ||
134 | { "setstat", NULL, SSH2_FXP_SETSTAT, process_setstat, 1 }, | ||
135 | { "fsetstat", NULL, SSH2_FXP_FSETSTAT, process_fsetstat, 1 }, | ||
136 | { "opendir", NULL, SSH2_FXP_OPENDIR, process_opendir, 0 }, | ||
137 | { "readdir", NULL, SSH2_FXP_READDIR, process_readdir, 0 }, | ||
138 | { "remove", NULL, SSH2_FXP_REMOVE, process_remove, 1 }, | ||
139 | { "mkdir", NULL, SSH2_FXP_MKDIR, process_mkdir, 1 }, | ||
140 | { "rmdir", NULL, SSH2_FXP_RMDIR, process_rmdir, 1 }, | ||
141 | { "realpath", NULL, SSH2_FXP_REALPATH, process_realpath, 0 }, | ||
142 | { "stat", NULL, SSH2_FXP_STAT, process_stat, 0 }, | ||
143 | { "rename", NULL, SSH2_FXP_RENAME, process_rename, 1 }, | ||
144 | { "readlink", NULL, SSH2_FXP_READLINK, process_readlink, 0 }, | ||
145 | { "symlink", NULL, SSH2_FXP_SYMLINK, process_symlink, 1 }, | ||
146 | { NULL, NULL, 0, NULL, 0 } | ||
147 | }; | ||
148 | |||
149 | /* SSH2_FXP_EXTENDED submessages */ | ||
150 | struct sftp_handler extended_handlers[] = { | ||
151 | { "posix-rename", "posix-rename@openssh.com", 0, | ||
152 | process_extended_posix_rename, 1 }, | ||
153 | { "statvfs", "statvfs@openssh.com", 0, process_extended_statvfs, 0 }, | ||
154 | { "fstatvfs", "fstatvfs@openssh.com", 0, process_extended_fstatvfs, 0 }, | ||
155 | { "hardlink", "hardlink@openssh.com", 0, process_extended_hardlink, 1 }, | ||
156 | { "fsync", "fsync@openssh.com", 0, process_extended_fsync, 1 }, | ||
157 | { NULL, NULL, 0, NULL, 0 } | ||
158 | }; | ||
159 | |||
160 | static int | ||
161 | request_permitted(struct sftp_handler *h) | ||
162 | { | ||
163 | char *result; | ||
164 | |||
165 | if (readonly && h->does_write) { | ||
166 | verbose("Refusing %s request in read-only mode", h->name); | ||
167 | return 0; | ||
168 | } | ||
169 | if (request_blacklist != NULL && | ||
170 | ((result = match_list(h->name, request_blacklist, NULL))) != NULL) { | ||
171 | free(result); | ||
172 | verbose("Refusing blacklisted %s request", h->name); | ||
173 | return 0; | ||
174 | } | ||
175 | if (request_whitelist != NULL && | ||
176 | ((result = match_list(h->name, request_whitelist, NULL))) != NULL) { | ||
177 | free(result); | ||
178 | debug2("Permitting whitelisted %s request", h->name); | ||
179 | return 1; | ||
180 | } | ||
181 | if (request_whitelist != NULL) { | ||
182 | verbose("Refusing non-whitelisted %s request", h->name); | ||
183 | return 0; | ||
184 | } | ||
185 | return 1; | ||
186 | } | ||
187 | |||
86 | static int | 188 | static int |
87 | errno_to_portable(int unixerrno) | 189 | errno_to_portable(int unixerrno) |
88 | { | 190 | { |
@@ -130,6 +232,8 @@ flags_from_portable(int pflags) | |||
130 | } else if (pflags & SSH2_FXF_WRITE) { | 232 | } else if (pflags & SSH2_FXF_WRITE) { |
131 | flags = O_WRONLY; | 233 | flags = O_WRONLY; |
132 | } | 234 | } |
235 | if (pflags & SSH2_FXF_APPEND) | ||
236 | flags |= O_APPEND; | ||
133 | if (pflags & SSH2_FXF_CREAT) | 237 | if (pflags & SSH2_FXF_CREAT) |
134 | flags |= O_CREAT; | 238 | flags |= O_CREAT; |
135 | if (pflags & SSH2_FXF_TRUNC) | 239 | if (pflags & SSH2_FXF_TRUNC) |
@@ -156,6 +260,8 @@ string_from_portable(int pflags) | |||
156 | PAPPEND("READ") | 260 | PAPPEND("READ") |
157 | if (pflags & SSH2_FXF_WRITE) | 261 | if (pflags & SSH2_FXF_WRITE) |
158 | PAPPEND("WRITE") | 262 | PAPPEND("WRITE") |
263 | if (pflags & SSH2_FXF_APPEND) | ||
264 | PAPPEND("APPEND") | ||
159 | if (pflags & SSH2_FXF_CREAT) | 265 | if (pflags & SSH2_FXF_CREAT) |
160 | PAPPEND("CREATE") | 266 | PAPPEND("CREATE") |
161 | if (pflags & SSH2_FXF_TRUNC) | 267 | if (pflags & SSH2_FXF_TRUNC) |
@@ -179,6 +285,7 @@ struct Handle { | |||
179 | int use; | 285 | int use; |
180 | DIR *dirp; | 286 | DIR *dirp; |
181 | int fd; | 287 | int fd; |
288 | int flags; | ||
182 | char *name; | 289 | char *name; |
183 | u_int64_t bytes_read, bytes_write; | 290 | u_int64_t bytes_read, bytes_write; |
184 | int next_unused; | 291 | int next_unused; |
@@ -202,7 +309,7 @@ static void handle_unused(int i) | |||
202 | } | 309 | } |
203 | 310 | ||
204 | static int | 311 | static int |
205 | handle_new(int use, const char *name, int fd, DIR *dirp) | 312 | handle_new(int use, const char *name, int fd, int flags, DIR *dirp) |
206 | { | 313 | { |
207 | int i; | 314 | int i; |
208 | 315 | ||
@@ -220,6 +327,7 @@ handle_new(int use, const char *name, int fd, DIR *dirp) | |||
220 | handles[i].use = use; | 327 | handles[i].use = use; |
221 | handles[i].dirp = dirp; | 328 | handles[i].dirp = dirp; |
222 | handles[i].fd = fd; | 329 | handles[i].fd = fd; |
330 | handles[i].flags = flags; | ||
223 | handles[i].name = xstrdup(name); | 331 | handles[i].name = xstrdup(name); |
224 | handles[i].bytes_read = handles[i].bytes_write = 0; | 332 | handles[i].bytes_read = handles[i].bytes_write = 0; |
225 | 333 | ||
@@ -282,6 +390,14 @@ handle_to_fd(int handle) | |||
282 | return -1; | 390 | return -1; |
283 | } | 391 | } |
284 | 392 | ||
393 | static int | ||
394 | handle_to_flags(int handle) | ||
395 | { | ||
396 | if (handle_is_ok(handle, HANDLE_FILE)) | ||
397 | return handles[handle].flags; | ||
398 | return 0; | ||
399 | } | ||
400 | |||
285 | static void | 401 | static void |
286 | handle_update_read(int handle, ssize_t bytes) | 402 | handle_update_read(int handle, ssize_t bytes) |
287 | { | 403 | { |
@@ -538,19 +654,21 @@ process_init(void) | |||
538 | /* hardlink extension */ | 654 | /* hardlink extension */ |
539 | buffer_put_cstring(&msg, "hardlink@openssh.com"); | 655 | buffer_put_cstring(&msg, "hardlink@openssh.com"); |
540 | buffer_put_cstring(&msg, "1"); /* version */ | 656 | buffer_put_cstring(&msg, "1"); /* version */ |
657 | /* fsync extension */ | ||
658 | buffer_put_cstring(&msg, "fsync@openssh.com"); | ||
659 | buffer_put_cstring(&msg, "1"); /* version */ | ||
541 | send_msg(&msg); | 660 | send_msg(&msg); |
542 | buffer_free(&msg); | 661 | buffer_free(&msg); |
543 | } | 662 | } |
544 | 663 | ||
545 | static void | 664 | static void |
546 | process_open(void) | 665 | process_open(u_int32_t id) |
547 | { | 666 | { |
548 | u_int32_t id, pflags; | 667 | u_int32_t pflags; |
549 | Attrib *a; | 668 | Attrib *a; |
550 | char *name; | 669 | char *name; |
551 | int handle, fd, flags, mode, status = SSH2_FX_FAILURE; | 670 | int handle, fd, flags, mode, status = SSH2_FX_FAILURE; |
552 | 671 | ||
553 | id = get_int(); | ||
554 | name = get_string(NULL); | 672 | name = get_string(NULL); |
555 | pflags = get_int(); /* portable flags */ | 673 | pflags = get_int(); /* portable flags */ |
556 | debug3("request %u: open flags %d", id, pflags); | 674 | debug3("request %u: open flags %d", id, pflags); |
@@ -560,14 +678,16 @@ process_open(void) | |||
560 | logit("open \"%s\" flags %s mode 0%o", | 678 | logit("open \"%s\" flags %s mode 0%o", |
561 | name, string_from_portable(pflags), mode); | 679 | name, string_from_portable(pflags), mode); |
562 | if (readonly && | 680 | if (readonly && |
563 | ((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR)) | 681 | ((flags & O_ACCMODE) == O_WRONLY || |
564 | status = SSH2_FX_PERMISSION_DENIED; | 682 | (flags & O_ACCMODE) == O_RDWR)) { |
565 | else { | 683 | verbose("Refusing open request in read-only mode"); |
684 | status = SSH2_FX_PERMISSION_DENIED; | ||
685 | } else { | ||
566 | fd = open(name, flags, mode); | 686 | fd = open(name, flags, mode); |
567 | if (fd < 0) { | 687 | if (fd < 0) { |
568 | status = errno_to_portable(errno); | 688 | status = errno_to_portable(errno); |
569 | } else { | 689 | } else { |
570 | handle = handle_new(HANDLE_FILE, name, fd, NULL); | 690 | handle = handle_new(HANDLE_FILE, name, fd, flags, NULL); |
571 | if (handle < 0) { | 691 | if (handle < 0) { |
572 | close(fd); | 692 | close(fd); |
573 | } else { | 693 | } else { |
@@ -582,12 +702,10 @@ process_open(void) | |||
582 | } | 702 | } |
583 | 703 | ||
584 | static void | 704 | static void |
585 | process_close(void) | 705 | process_close(u_int32_t id) |
586 | { | 706 | { |
587 | u_int32_t id; | ||
588 | int handle, ret, status = SSH2_FX_FAILURE; | 707 | int handle, ret, status = SSH2_FX_FAILURE; |
589 | 708 | ||
590 | id = get_int(); | ||
591 | handle = get_handle(); | 709 | handle = get_handle(); |
592 | debug3("request %u: close handle %u", id, handle); | 710 | debug3("request %u: close handle %u", id, handle); |
593 | handle_log_close(handle, NULL); | 711 | handle_log_close(handle, NULL); |
@@ -597,14 +715,13 @@ process_close(void) | |||
597 | } | 715 | } |
598 | 716 | ||
599 | static void | 717 | static void |
600 | process_read(void) | 718 | process_read(u_int32_t id) |
601 | { | 719 | { |
602 | char buf[64*1024]; | 720 | char buf[64*1024]; |
603 | u_int32_t id, len; | 721 | u_int32_t len; |
604 | int handle, fd, ret, status = SSH2_FX_FAILURE; | 722 | int handle, fd, ret, status = SSH2_FX_FAILURE; |
605 | u_int64_t off; | 723 | u_int64_t off; |
606 | 724 | ||
607 | id = get_int(); | ||
608 | handle = get_handle(); | 725 | handle = get_handle(); |
609 | off = get_int64(); | 726 | off = get_int64(); |
610 | len = get_int(); | 727 | len = get_int(); |
@@ -638,15 +755,13 @@ process_read(void) | |||
638 | } | 755 | } |
639 | 756 | ||
640 | static void | 757 | static void |
641 | process_write(void) | 758 | process_write(u_int32_t id) |
642 | { | 759 | { |
643 | u_int32_t id; | ||
644 | u_int64_t off; | 760 | u_int64_t off; |
645 | u_int len; | 761 | u_int len; |
646 | int handle, fd, ret, status; | 762 | int handle, fd, ret, status; |
647 | char *data; | 763 | char *data; |
648 | 764 | ||
649 | id = get_int(); | ||
650 | handle = get_handle(); | 765 | handle = get_handle(); |
651 | off = get_int64(); | 766 | off = get_int64(); |
652 | data = get_string(&len); | 767 | data = get_string(&len); |
@@ -657,10 +772,9 @@ process_write(void) | |||
657 | 772 | ||
658 | if (fd < 0) | 773 | if (fd < 0) |
659 | status = SSH2_FX_FAILURE; | 774 | status = SSH2_FX_FAILURE; |
660 | else if (readonly) | ||
661 | status = SSH2_FX_PERMISSION_DENIED; | ||
662 | else { | 775 | else { |
663 | if (lseek(fd, off, SEEK_SET) < 0) { | 776 | if (!(handle_to_flags(handle) & O_APPEND) && |
777 | lseek(fd, off, SEEK_SET) < 0) { | ||
664 | status = errno_to_portable(errno); | 778 | status = errno_to_portable(errno); |
665 | error("process_write: seek failed"); | 779 | error("process_write: seek failed"); |
666 | } else { | 780 | } else { |
@@ -683,15 +797,13 @@ process_write(void) | |||
683 | } | 797 | } |
684 | 798 | ||
685 | static void | 799 | static void |
686 | process_do_stat(int do_lstat) | 800 | process_do_stat(u_int32_t id, int do_lstat) |
687 | { | 801 | { |
688 | Attrib a; | 802 | Attrib a; |
689 | struct stat st; | 803 | struct stat st; |
690 | u_int32_t id; | ||
691 | char *name; | 804 | char *name; |
692 | int ret, status = SSH2_FX_FAILURE; | 805 | int ret, status = SSH2_FX_FAILURE; |
693 | 806 | ||
694 | id = get_int(); | ||
695 | name = get_string(NULL); | 807 | name = get_string(NULL); |
696 | debug3("request %u: %sstat", id, do_lstat ? "l" : ""); | 808 | debug3("request %u: %sstat", id, do_lstat ? "l" : ""); |
697 | verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); | 809 | verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); |
@@ -709,26 +821,24 @@ process_do_stat(int do_lstat) | |||
709 | } | 821 | } |
710 | 822 | ||
711 | static void | 823 | static void |
712 | process_stat(void) | 824 | process_stat(u_int32_t id) |
713 | { | 825 | { |
714 | process_do_stat(0); | 826 | process_do_stat(id, 0); |
715 | } | 827 | } |
716 | 828 | ||
717 | static void | 829 | static void |
718 | process_lstat(void) | 830 | process_lstat(u_int32_t id) |
719 | { | 831 | { |
720 | process_do_stat(1); | 832 | process_do_stat(id, 1); |
721 | } | 833 | } |
722 | 834 | ||
723 | static void | 835 | static void |
724 | process_fstat(void) | 836 | process_fstat(u_int32_t id) |
725 | { | 837 | { |
726 | Attrib a; | 838 | Attrib a; |
727 | struct stat st; | 839 | struct stat st; |
728 | u_int32_t id; | ||
729 | int fd, ret, handle, status = SSH2_FX_FAILURE; | 840 | int fd, ret, handle, status = SSH2_FX_FAILURE; |
730 | 841 | ||
731 | id = get_int(); | ||
732 | handle = get_handle(); | 842 | handle = get_handle(); |
733 | debug("request %u: fstat \"%s\" (handle %u)", | 843 | debug("request %u: fstat \"%s\" (handle %u)", |
734 | id, handle_to_name(handle), handle); | 844 | id, handle_to_name(handle), handle); |
@@ -760,21 +870,15 @@ attrib_to_tv(const Attrib *a) | |||
760 | } | 870 | } |
761 | 871 | ||
762 | static void | 872 | static void |
763 | process_setstat(void) | 873 | process_setstat(u_int32_t id) |
764 | { | 874 | { |
765 | Attrib *a; | 875 | Attrib *a; |
766 | u_int32_t id; | ||
767 | char *name; | 876 | char *name; |
768 | int status = SSH2_FX_OK, ret; | 877 | int status = SSH2_FX_OK, ret; |
769 | 878 | ||
770 | id = get_int(); | ||
771 | name = get_string(NULL); | 879 | name = get_string(NULL); |
772 | a = get_attrib(); | 880 | a = get_attrib(); |
773 | debug("request %u: setstat name \"%s\"", id, name); | 881 | 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) { | 882 | if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { |
779 | logit("set \"%s\" size %llu", | 883 | logit("set \"%s\" size %llu", |
780 | name, (unsigned long long)a->size); | 884 | name, (unsigned long long)a->size); |
@@ -811,22 +915,18 @@ process_setstat(void) | |||
811 | } | 915 | } |
812 | 916 | ||
813 | static void | 917 | static void |
814 | process_fsetstat(void) | 918 | process_fsetstat(u_int32_t id) |
815 | { | 919 | { |
816 | Attrib *a; | 920 | Attrib *a; |
817 | u_int32_t id; | ||
818 | int handle, fd, ret; | 921 | int handle, fd, ret; |
819 | int status = SSH2_FX_OK; | 922 | int status = SSH2_FX_OK; |
820 | 923 | ||
821 | id = get_int(); | ||
822 | handle = get_handle(); | 924 | handle = get_handle(); |
823 | a = get_attrib(); | 925 | a = get_attrib(); |
824 | debug("request %u: fsetstat handle %d", id, handle); | 926 | debug("request %u: fsetstat handle %d", id, handle); |
825 | fd = handle_to_fd(handle); | 927 | fd = handle_to_fd(handle); |
826 | if (fd < 0) | 928 | if (fd < 0) |
827 | status = SSH2_FX_FAILURE; | 929 | status = SSH2_FX_FAILURE; |
828 | else if (readonly) | ||
829 | status = SSH2_FX_PERMISSION_DENIED; | ||
830 | else { | 930 | else { |
831 | char *name = handle_to_name(handle); | 931 | char *name = handle_to_name(handle); |
832 | 932 | ||
@@ -878,14 +978,12 @@ process_fsetstat(void) | |||
878 | } | 978 | } |
879 | 979 | ||
880 | static void | 980 | static void |
881 | process_opendir(void) | 981 | process_opendir(u_int32_t id) |
882 | { | 982 | { |
883 | DIR *dirp = NULL; | 983 | DIR *dirp = NULL; |
884 | char *path; | 984 | char *path; |
885 | int handle, status = SSH2_FX_FAILURE; | 985 | int handle, status = SSH2_FX_FAILURE; |
886 | u_int32_t id; | ||
887 | 986 | ||
888 | id = get_int(); | ||
889 | path = get_string(NULL); | 987 | path = get_string(NULL); |
890 | debug3("request %u: opendir", id); | 988 | debug3("request %u: opendir", id); |
891 | logit("opendir \"%s\"", path); | 989 | logit("opendir \"%s\"", path); |
@@ -893,7 +991,7 @@ process_opendir(void) | |||
893 | if (dirp == NULL) { | 991 | if (dirp == NULL) { |
894 | status = errno_to_portable(errno); | 992 | status = errno_to_portable(errno); |
895 | } else { | 993 | } else { |
896 | handle = handle_new(HANDLE_DIR, path, 0, dirp); | 994 | handle = handle_new(HANDLE_DIR, path, 0, 0, dirp); |
897 | if (handle < 0) { | 995 | if (handle < 0) { |
898 | closedir(dirp); | 996 | closedir(dirp); |
899 | } else { | 997 | } else { |
@@ -908,15 +1006,13 @@ process_opendir(void) | |||
908 | } | 1006 | } |
909 | 1007 | ||
910 | static void | 1008 | static void |
911 | process_readdir(void) | 1009 | process_readdir(u_int32_t id) |
912 | { | 1010 | { |
913 | DIR *dirp; | 1011 | DIR *dirp; |
914 | struct dirent *dp; | 1012 | struct dirent *dp; |
915 | char *path; | 1013 | char *path; |
916 | int handle; | 1014 | int handle; |
917 | u_int32_t id; | ||
918 | 1015 | ||
919 | id = get_int(); | ||
920 | handle = get_handle(); | 1016 | handle = get_handle(); |
921 | debug("request %u: readdir \"%s\" (handle %d)", id, | 1017 | debug("request %u: readdir \"%s\" (handle %d)", id, |
922 | handle_to_name(handle), handle); | 1018 | handle_to_name(handle), handle); |
@@ -964,81 +1060,61 @@ process_readdir(void) | |||
964 | } | 1060 | } |
965 | 1061 | ||
966 | static void | 1062 | static void |
967 | process_remove(void) | 1063 | process_remove(u_int32_t id) |
968 | { | 1064 | { |
969 | char *name; | 1065 | char *name; |
970 | u_int32_t id; | ||
971 | int status = SSH2_FX_FAILURE; | 1066 | int status = SSH2_FX_FAILURE; |
972 | int ret; | 1067 | int ret; |
973 | 1068 | ||
974 | id = get_int(); | ||
975 | name = get_string(NULL); | 1069 | name = get_string(NULL); |
976 | debug3("request %u: remove", id); | 1070 | debug3("request %u: remove", id); |
977 | logit("remove name \"%s\"", name); | 1071 | logit("remove name \"%s\"", name); |
978 | if (readonly) | 1072 | ret = unlink(name); |
979 | status = SSH2_FX_PERMISSION_DENIED; | 1073 | 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); | 1074 | send_status(id, status); |
985 | free(name); | 1075 | free(name); |
986 | } | 1076 | } |
987 | 1077 | ||
988 | static void | 1078 | static void |
989 | process_mkdir(void) | 1079 | process_mkdir(u_int32_t id) |
990 | { | 1080 | { |
991 | Attrib *a; | 1081 | Attrib *a; |
992 | u_int32_t id; | ||
993 | char *name; | 1082 | char *name; |
994 | int ret, mode, status = SSH2_FX_FAILURE; | 1083 | int ret, mode, status = SSH2_FX_FAILURE; |
995 | 1084 | ||
996 | id = get_int(); | ||
997 | name = get_string(NULL); | 1085 | name = get_string(NULL); |
998 | a = get_attrib(); | 1086 | a = get_attrib(); |
999 | mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? | 1087 | mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? |
1000 | a->perm & 07777 : 0777; | 1088 | a->perm & 07777 : 0777; |
1001 | debug3("request %u: mkdir", id); | 1089 | debug3("request %u: mkdir", id); |
1002 | logit("mkdir name \"%s\" mode 0%o", name, mode); | 1090 | logit("mkdir name \"%s\" mode 0%o", name, mode); |
1003 | if (readonly) | 1091 | ret = mkdir(name, mode); |
1004 | status = SSH2_FX_PERMISSION_DENIED; | 1092 | 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); | 1093 | send_status(id, status); |
1010 | free(name); | 1094 | free(name); |
1011 | } | 1095 | } |
1012 | 1096 | ||
1013 | static void | 1097 | static void |
1014 | process_rmdir(void) | 1098 | process_rmdir(u_int32_t id) |
1015 | { | 1099 | { |
1016 | u_int32_t id; | ||
1017 | char *name; | 1100 | char *name; |
1018 | int ret, status; | 1101 | int ret, status; |
1019 | 1102 | ||
1020 | id = get_int(); | ||
1021 | name = get_string(NULL); | 1103 | name = get_string(NULL); |
1022 | debug3("request %u: rmdir", id); | 1104 | debug3("request %u: rmdir", id); |
1023 | logit("rmdir name \"%s\"", name); | 1105 | logit("rmdir name \"%s\"", name); |
1024 | if (readonly) | 1106 | ret = rmdir(name); |
1025 | status = SSH2_FX_PERMISSION_DENIED; | 1107 | 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); | 1108 | send_status(id, status); |
1031 | free(name); | 1109 | free(name); |
1032 | } | 1110 | } |
1033 | 1111 | ||
1034 | static void | 1112 | static void |
1035 | process_realpath(void) | 1113 | process_realpath(u_int32_t id) |
1036 | { | 1114 | { |
1037 | char resolvedname[MAXPATHLEN]; | 1115 | char resolvedname[MAXPATHLEN]; |
1038 | u_int32_t id; | ||
1039 | char *path; | 1116 | char *path; |
1040 | 1117 | ||
1041 | id = get_int(); | ||
1042 | path = get_string(NULL); | 1118 | path = get_string(NULL); |
1043 | if (path[0] == '\0') { | 1119 | if (path[0] == '\0') { |
1044 | free(path); | 1120 | free(path); |
@@ -1058,22 +1134,18 @@ process_realpath(void) | |||
1058 | } | 1134 | } |
1059 | 1135 | ||
1060 | static void | 1136 | static void |
1061 | process_rename(void) | 1137 | process_rename(u_int32_t id) |
1062 | { | 1138 | { |
1063 | u_int32_t id; | ||
1064 | char *oldpath, *newpath; | 1139 | char *oldpath, *newpath; |
1065 | int status; | 1140 | int status; |
1066 | struct stat sb; | 1141 | struct stat sb; |
1067 | 1142 | ||
1068 | id = get_int(); | ||
1069 | oldpath = get_string(NULL); | 1143 | oldpath = get_string(NULL); |
1070 | newpath = get_string(NULL); | 1144 | newpath = get_string(NULL); |
1071 | debug3("request %u: rename", id); | 1145 | debug3("request %u: rename", id); |
1072 | logit("rename old \"%s\" new \"%s\"", oldpath, newpath); | 1146 | logit("rename old \"%s\" new \"%s\"", oldpath, newpath); |
1073 | status = SSH2_FX_FAILURE; | 1147 | status = SSH2_FX_FAILURE; |
1074 | if (readonly) | 1148 | if (lstat(oldpath, &sb) == -1) |
1075 | status = SSH2_FX_PERMISSION_DENIED; | ||
1076 | else if (lstat(oldpath, &sb) == -1) | ||
1077 | status = errno_to_portable(errno); | 1149 | status = errno_to_portable(errno); |
1078 | else if (S_ISREG(sb.st_mode)) { | 1150 | else if (S_ISREG(sb.st_mode)) { |
1079 | /* Race-free rename of regular files */ | 1151 | /* Race-free rename of regular files */ |
@@ -1120,14 +1192,12 @@ process_rename(void) | |||
1120 | } | 1192 | } |
1121 | 1193 | ||
1122 | static void | 1194 | static void |
1123 | process_readlink(void) | 1195 | process_readlink(u_int32_t id) |
1124 | { | 1196 | { |
1125 | u_int32_t id; | ||
1126 | int len; | 1197 | int len; |
1127 | char buf[MAXPATHLEN]; | 1198 | char buf[MAXPATHLEN]; |
1128 | char *path; | 1199 | char *path; |
1129 | 1200 | ||
1130 | id = get_int(); | ||
1131 | path = get_string(NULL); | 1201 | path = get_string(NULL); |
1132 | debug3("request %u: readlink", id); | 1202 | debug3("request %u: readlink", id); |
1133 | verbose("readlink \"%s\"", path); | 1203 | verbose("readlink \"%s\"", path); |
@@ -1145,24 +1215,18 @@ process_readlink(void) | |||
1145 | } | 1215 | } |
1146 | 1216 | ||
1147 | static void | 1217 | static void |
1148 | process_symlink(void) | 1218 | process_symlink(u_int32_t id) |
1149 | { | 1219 | { |
1150 | u_int32_t id; | ||
1151 | char *oldpath, *newpath; | 1220 | char *oldpath, *newpath; |
1152 | int ret, status; | 1221 | int ret, status; |
1153 | 1222 | ||
1154 | id = get_int(); | ||
1155 | oldpath = get_string(NULL); | 1223 | oldpath = get_string(NULL); |
1156 | newpath = get_string(NULL); | 1224 | newpath = get_string(NULL); |
1157 | debug3("request %u: symlink", id); | 1225 | debug3("request %u: symlink", id); |
1158 | logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); | 1226 | logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); |
1159 | /* this will fail if 'newpath' exists */ | 1227 | /* this will fail if 'newpath' exists */ |
1160 | if (readonly) | 1228 | ret = symlink(oldpath, newpath); |
1161 | status = SSH2_FX_PERMISSION_DENIED; | 1229 | 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); | 1230 | send_status(id, status); |
1167 | free(oldpath); | 1231 | free(oldpath); |
1168 | free(newpath); | 1232 | free(newpath); |
@@ -1178,12 +1242,8 @@ process_extended_posix_rename(u_int32_t id) | |||
1178 | newpath = get_string(NULL); | 1242 | newpath = get_string(NULL); |
1179 | debug3("request %u: posix-rename", id); | 1243 | debug3("request %u: posix-rename", id); |
1180 | logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); | 1244 | logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); |
1181 | if (readonly) | 1245 | ret = rename(oldpath, newpath); |
1182 | status = SSH2_FX_PERMISSION_DENIED; | 1246 | 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); | 1247 | send_status(id, status); |
1188 | free(oldpath); | 1248 | free(oldpath); |
1189 | free(newpath); | 1249 | free(newpath); |
@@ -1196,8 +1256,8 @@ process_extended_statvfs(u_int32_t id) | |||
1196 | struct statvfs st; | 1256 | struct statvfs st; |
1197 | 1257 | ||
1198 | path = get_string(NULL); | 1258 | path = get_string(NULL); |
1199 | debug3("request %u: statfs", id); | 1259 | debug3("request %u: statvfs", id); |
1200 | logit("statfs \"%s\"", path); | 1260 | logit("statvfs \"%s\"", path); |
1201 | 1261 | ||
1202 | if (statvfs(path, &st) != 0) | 1262 | if (statvfs(path, &st) != 0) |
1203 | send_status(id, errno_to_portable(errno)); | 1263 | send_status(id, errno_to_portable(errno)); |
@@ -1235,35 +1295,50 @@ process_extended_hardlink(u_int32_t id) | |||
1235 | newpath = get_string(NULL); | 1295 | newpath = get_string(NULL); |
1236 | debug3("request %u: hardlink", id); | 1296 | debug3("request %u: hardlink", id); |
1237 | logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath); | 1297 | logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath); |
1238 | if (readonly) | 1298 | ret = link(oldpath, newpath); |
1239 | status = SSH2_FX_PERMISSION_DENIED; | 1299 | 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); | 1300 | send_status(id, status); |
1245 | free(oldpath); | 1301 | free(oldpath); |
1246 | free(newpath); | 1302 | free(newpath); |
1247 | } | 1303 | } |
1248 | 1304 | ||
1249 | static void | 1305 | static void |
1250 | process_extended(void) | 1306 | process_extended_fsync(u_int32_t id) |
1307 | { | ||
1308 | int handle, fd, ret, status = SSH2_FX_OP_UNSUPPORTED; | ||
1309 | |||
1310 | handle = get_handle(); | ||
1311 | debug3("request %u: fsync (handle %u)", id, handle); | ||
1312 | verbose("fsync \"%s\"", handle_to_name(handle)); | ||
1313 | if ((fd = handle_to_fd(handle)) < 0) | ||
1314 | status = SSH2_FX_NO_SUCH_FILE; | ||
1315 | else if (handle_is_ok(handle, HANDLE_FILE)) { | ||
1316 | ret = fsync(fd); | ||
1317 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; | ||
1318 | } | ||
1319 | send_status(id, status); | ||
1320 | } | ||
1321 | |||
1322 | static void | ||
1323 | process_extended(u_int32_t id) | ||
1251 | { | 1324 | { |
1252 | u_int32_t id; | ||
1253 | char *request; | 1325 | char *request; |
1326 | u_int i; | ||
1254 | 1327 | ||
1255 | id = get_int(); | ||
1256 | request = get_string(NULL); | 1328 | request = get_string(NULL); |
1257 | if (strcmp(request, "posix-rename@openssh.com") == 0) | 1329 | for (i = 0; extended_handlers[i].handler != NULL; i++) { |
1258 | process_extended_posix_rename(id); | 1330 | if (strcmp(request, extended_handlers[i].ext_name) == 0) { |
1259 | else if (strcmp(request, "statvfs@openssh.com") == 0) | 1331 | if (!request_permitted(&extended_handlers[i])) |
1260 | process_extended_statvfs(id); | 1332 | send_status(id, SSH2_FX_PERMISSION_DENIED); |
1261 | else if (strcmp(request, "fstatvfs@openssh.com") == 0) | 1333 | else |
1262 | process_extended_fstatvfs(id); | 1334 | extended_handlers[i].handler(id); |
1263 | else if (strcmp(request, "hardlink@openssh.com") == 0) | 1335 | break; |
1264 | process_extended_hardlink(id); | 1336 | } |
1265 | else | 1337 | } |
1338 | if (extended_handlers[i].handler == NULL) { | ||
1339 | error("Unknown extended request \"%.100s\"", request); | ||
1266 | send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ | 1340 | send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ |
1341 | } | ||
1267 | free(request); | 1342 | free(request); |
1268 | } | 1343 | } |
1269 | 1344 | ||
@@ -1272,11 +1347,9 @@ process_extended(void) | |||
1272 | static void | 1347 | static void |
1273 | process(void) | 1348 | process(void) |
1274 | { | 1349 | { |
1275 | u_int msg_len; | 1350 | 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; | 1351 | u_char *cp; |
1352 | u_int32_t id; | ||
1280 | 1353 | ||
1281 | buf_len = buffer_len(&iqueue); | 1354 | buf_len = buffer_len(&iqueue); |
1282 | if (buf_len < 5) | 1355 | if (buf_len < 5) |
@@ -1293,70 +1366,35 @@ process(void) | |||
1293 | buffer_consume(&iqueue, 4); | 1366 | buffer_consume(&iqueue, 4); |
1294 | buf_len -= 4; | 1367 | buf_len -= 4; |
1295 | type = buffer_get_char(&iqueue); | 1368 | type = buffer_get_char(&iqueue); |
1369 | |||
1296 | switch (type) { | 1370 | switch (type) { |
1297 | case SSH2_FXP_INIT: | 1371 | case SSH2_FXP_INIT: |
1298 | process_init(); | 1372 | process_init(); |
1299 | break; | 1373 | 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; | 1374 | break; |
1354 | case SSH2_FXP_EXTENDED: | 1375 | case SSH2_FXP_EXTENDED: |
1355 | process_extended(); | 1376 | if (!init_done) |
1377 | fatal("Received extended request before init"); | ||
1378 | id = get_int(); | ||
1379 | process_extended(id); | ||
1356 | break; | 1380 | break; |
1357 | default: | 1381 | default: |
1358 | error("Unknown message %d", type); | 1382 | if (!init_done) |
1359 | break; | 1383 | fatal("Received %u request before init", type); |
1384 | id = get_int(); | ||
1385 | for (i = 0; handlers[i].handler != NULL; i++) { | ||
1386 | if (type == handlers[i].type) { | ||
1387 | if (!request_permitted(&handlers[i])) { | ||
1388 | send_status(id, | ||
1389 | SSH2_FX_PERMISSION_DENIED); | ||
1390 | } else { | ||
1391 | handlers[i].handler(id); | ||
1392 | } | ||
1393 | break; | ||
1394 | } | ||
1395 | } | ||
1396 | if (handlers[i].handler == NULL) | ||
1397 | error("Unknown message %u", type); | ||
1360 | } | 1398 | } |
1361 | /* discard the remaining bytes from the current packet */ | 1399 | /* discard the remaining bytes from the current packet */ |
1362 | if (buf_len < buffer_len(&iqueue)) { | 1400 | if (buf_len < buffer_len(&iqueue)) { |
@@ -1365,7 +1403,7 @@ process(void) | |||
1365 | } | 1403 | } |
1366 | consumed = buf_len - buffer_len(&iqueue); | 1404 | consumed = buf_len - buffer_len(&iqueue); |
1367 | if (msg_len < consumed) { | 1405 | if (msg_len < consumed) { |
1368 | error("msg_len %d < consumed %d", msg_len, consumed); | 1406 | error("msg_len %u < consumed %u", msg_len, consumed); |
1369 | sftp_server_cleanup_exit(255); | 1407 | sftp_server_cleanup_exit(255); |
1370 | } | 1408 | } |
1371 | if (msg_len > consumed) | 1409 | if (msg_len > consumed) |
@@ -1391,8 +1429,10 @@ sftp_server_usage(void) | |||
1391 | 1429 | ||
1392 | fprintf(stderr, | 1430 | fprintf(stderr, |
1393 | "usage: %s [-ehR] [-d start_directory] [-f log_facility] " | 1431 | "usage: %s [-ehR] [-d start_directory] [-f log_facility] " |
1394 | "[-l log_level]\n\t[-u umask]\n", | 1432 | "[-l log_level]\n\t[-P blacklisted_requests] " |
1395 | __progname); | 1433 | "[-p whitelisted_requests] [-u umask]\n" |
1434 | " %s -Q protocol_feature\n", | ||
1435 | __progname, __progname); | ||
1396 | exit(1); | 1436 | exit(1); |
1397 | } | 1437 | } |
1398 | 1438 | ||
@@ -1400,7 +1440,7 @@ int | |||
1400 | sftp_server_main(int argc, char **argv, struct passwd *user_pw) | 1440 | sftp_server_main(int argc, char **argv, struct passwd *user_pw) |
1401 | { | 1441 | { |
1402 | fd_set *rset, *wset; | 1442 | fd_set *rset, *wset; |
1403 | int in, out, max, ch, skipargs = 0, log_stderr = 0; | 1443 | int i, in, out, max, ch, skipargs = 0, log_stderr = 0; |
1404 | ssize_t len, olen, set_size; | 1444 | ssize_t len, olen, set_size; |
1405 | SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; | 1445 | SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; |
1406 | char *cp, *homedir = NULL, buf[4*4096]; | 1446 | char *cp, *homedir = NULL, buf[4*4096]; |
@@ -1414,8 +1454,20 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) | |||
1414 | 1454 | ||
1415 | pw = pwcopy(user_pw); | 1455 | pw = pwcopy(user_pw); |
1416 | 1456 | ||
1417 | while (!skipargs && (ch = getopt(argc, argv, "d:f:l:u:cehR")) != -1) { | 1457 | while (!skipargs && (ch = getopt(argc, argv, |
1458 | "d:f:l:P:p:Q:u:cehR")) != -1) { | ||
1418 | switch (ch) { | 1459 | switch (ch) { |
1460 | case 'Q': | ||
1461 | if (strcasecmp(optarg, "requests") != 0) { | ||
1462 | fprintf(stderr, "Invalid query type\n"); | ||
1463 | exit(1); | ||
1464 | } | ||
1465 | for (i = 0; handlers[i].handler != NULL; i++) | ||
1466 | printf("%s\n", handlers[i].name); | ||
1467 | for (i = 0; extended_handlers[i].handler != NULL; i++) | ||
1468 | printf("%s\n", extended_handlers[i].name); | ||
1469 | exit(0); | ||
1470 | break; | ||
1419 | case 'R': | 1471 | case 'R': |
1420 | readonly = 1; | 1472 | readonly = 1; |
1421 | break; | 1473 | break; |
@@ -1445,6 +1497,16 @@ sftp_server_main(int argc, char **argv, struct passwd *user_pw) | |||
1445 | "u", user_pw->pw_name, (char *)NULL); | 1497 | "u", user_pw->pw_name, (char *)NULL); |
1446 | free(cp); | 1498 | free(cp); |
1447 | break; | 1499 | break; |
1500 | case 'p': | ||
1501 | if (request_whitelist != NULL) | ||
1502 | fatal("Permitted requests already set"); | ||
1503 | request_whitelist = xstrdup(optarg); | ||
1504 | break; | ||
1505 | case 'P': | ||
1506 | if (request_blacklist != NULL) | ||
1507 | fatal("Refused requests already set"); | ||
1508 | request_blacklist = xstrdup(optarg); | ||
1509 | break; | ||
1448 | case 'u': | 1510 | case 'u': |
1449 | errno = 0; | 1511 | errno = 0; |
1450 | mask = strtol(optarg, &cp, 8); | 1512 | mask = strtol(optarg, &cp, 8); |