2 * Copyright (c) 2000 Markus Friedl. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 RCSID("$OpenBSD: sftp-server.c,v 1.10 2001/01/10 22:56:22 markus Exp $");
36 #define get_int64() buffer_get_int64(&iqueue);
37 #define get_int() buffer_get_int(&iqueue);
38 #define get_string(lenp) buffer_get_string(&iqueue, lenp);
41 #ifdef HAVE___PROGNAME
42 extern char *__progname;
47 /* input and output queue */
51 /* portable attibutes, etc. */
53 typedef struct Attrib Attrib;
54 typedef struct Stat Stat;
75 errno_to_portable(int unixerrno)
86 ret = SSH2_FX_NO_SUCH_FILE;
91 ret = SSH2_FX_PERMISSION_DENIED;
95 ret = SSH2_FX_BAD_MESSAGE;
98 ret = SSH2_FX_FAILURE;
105 flags_from_portable(int pflags)
108 if (pflags & SSH2_FXF_READ &&
109 pflags & SSH2_FXF_WRITE) {
111 } else if (pflags & SSH2_FXF_READ) {
113 } else if (pflags & SSH2_FXF_WRITE) {
116 if (pflags & SSH2_FXF_CREAT)
118 if (pflags & SSH2_FXF_TRUNC)
120 if (pflags & SSH2_FXF_EXCL)
126 attrib_clear(Attrib *a)
138 decode_attrib(Buffer *b)
142 a.flags = buffer_get_int(b);
143 if (a.flags & SSH2_FILEXFER_ATTR_SIZE) {
144 a.size = buffer_get_int64(b);
146 if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) {
147 a.uid = buffer_get_int(b);
148 a.gid = buffer_get_int(b);
150 if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
151 a.perm = buffer_get_int(b);
153 if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
154 a.atime = buffer_get_int(b);
155 a.mtime = buffer_get_int(b);
157 /* vendor-specific extensions */
158 if (a.flags & SSH2_FILEXFER_ATTR_EXTENDED) {
161 count = buffer_get_int(b);
162 for (i = 0; i < count; i++) {
163 type = buffer_get_string(b, NULL);
164 data = buffer_get_string(b, NULL);
173 encode_attrib(Buffer *b, Attrib *a)
175 buffer_put_int(b, a->flags);
176 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
177 buffer_put_int64(b, a->size);
179 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
180 buffer_put_int(b, a->uid);
181 buffer_put_int(b, a->gid);
183 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
184 buffer_put_int(b, a->perm);
186 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
187 buffer_put_int(b, a->atime);
188 buffer_put_int(b, a->mtime);
193 stat_to_attrib(struct stat *st)
198 a.flags |= SSH2_FILEXFER_ATTR_SIZE;
199 a.size = st->st_size;
200 a.flags |= SSH2_FILEXFER_ATTR_UIDGID;
203 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
204 a.perm = st->st_mode;
205 a.flags |= SSH2_FILEXFER_ATTR_ACMODTIME;
206 a.atime = st->st_atime;
207 a.mtime = st->st_mtime;
214 return decode_attrib(&iqueue);
219 typedef struct Handle Handle;
237 for(i = 0; i < sizeof(handles)/sizeof(Handle); i++)
238 handles[i].use = HANDLE_UNUSED;
242 handle_new(int use, char *name, int fd, DIR *dirp)
245 for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
246 if (handles[i].use == HANDLE_UNUSED) {
247 handles[i].use = use;
248 handles[i].dirp = dirp;
250 handles[i].name = name;
258 handle_is_ok(int i, int type)
260 return i >= 0 && i < sizeof(handles)/sizeof(Handle) &&
261 handles[i].use == type;
265 handle_to_string(int handle, char **stringp, int *hlenp)
268 if (stringp == NULL || hlenp == NULL)
270 snprintf(buf, sizeof buf, "%d", handle);
271 *stringp = xstrdup(buf);
272 *hlenp = strlen(*stringp);
277 handle_from_string(char *handle, u_int hlen)
281 long lval = strtol(handle, &ep, 10);
285 if (handle_is_ok(val, HANDLE_FILE) ||
286 handle_is_ok(val, HANDLE_DIR))
292 handle_to_name(int handle)
294 if (handle_is_ok(handle, HANDLE_DIR)||
295 handle_is_ok(handle, HANDLE_FILE))
296 return handles[handle].name;
301 handle_to_dir(int handle)
303 if (handle_is_ok(handle, HANDLE_DIR))
304 return handles[handle].dirp;
309 handle_to_fd(int handle)
311 if (handle_is_ok(handle, HANDLE_FILE))
312 return handles[handle].fd;
317 handle_close(int handle)
320 if (handle_is_ok(handle, HANDLE_FILE)) {
321 ret = close(handles[handle].fd);
322 handles[handle].use = HANDLE_UNUSED;
323 } else if (handle_is_ok(handle, HANDLE_DIR)) {
324 ret = closedir(handles[handle].dirp);
325 handles[handle].use = HANDLE_UNUSED;
338 handle = get_string(&hlen);
340 val = handle_from_string(handle, hlen);
350 int mlen = buffer_len(m);
351 buffer_put_int(&oqueue, mlen);
352 buffer_append(&oqueue, buffer_ptr(m), mlen);
353 buffer_consume(m, mlen);
357 send_status(u_int32_t id, u_int32_t error)
360 TRACE("sent status id %d error %d", id, error);
362 buffer_put_char(&msg, SSH2_FXP_STATUS);
363 buffer_put_int(&msg, id);
364 buffer_put_int(&msg, error);
369 send_data_or_handle(char type, u_int32_t id, char *data, int dlen)
373 buffer_put_char(&msg, type);
374 buffer_put_int(&msg, id);
375 buffer_put_string(&msg, data, dlen);
381 send_data(u_int32_t id, char *data, int dlen)
383 TRACE("sent data id %d len %d", id, dlen);
384 send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
388 send_handle(u_int32_t id, int handle)
392 handle_to_string(handle, &string, &hlen);
393 TRACE("sent handle id %d handle %d", id, handle);
394 send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
399 send_names(u_int32_t id, int count, Stat *stats)
404 buffer_put_char(&msg, SSH2_FXP_NAME);
405 buffer_put_int(&msg, id);
406 buffer_put_int(&msg, count);
407 TRACE("sent names id %d count %d", id, count);
408 for (i = 0; i < count; i++) {
409 buffer_put_cstring(&msg, stats[i].name);
410 buffer_put_cstring(&msg, stats[i].long_name);
411 encode_attrib(&msg, &stats[i].attrib);
418 send_attrib(u_int32_t id, Attrib *a)
421 TRACE("sent attrib id %d have 0x%x", id, a->flags);
423 buffer_put_char(&msg, SSH2_FXP_ATTRS);
424 buffer_put_int(&msg, id);
425 encode_attrib(&msg, a);
436 int version = buffer_get_int(&iqueue);
438 TRACE("client version %d", version);
440 buffer_put_char(&msg, SSH2_FXP_VERSION);
441 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
449 u_int32_t id, pflags;
452 int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
455 name = get_string(NULL);
456 pflags = get_int(); /* portable flags */
458 flags = flags_from_portable(pflags);
459 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
460 TRACE("open id %d name %s flags %d mode 0%o", id, name, pflags, mode);
461 fd = open(name, flags, mode);
463 status = errno_to_portable(errno);
465 handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL);
469 send_handle(id, handle);
473 if (status != SSH2_FX_OK)
474 send_status(id, status);
482 int handle, ret, status = SSH2_FX_FAILURE;
485 handle = get_handle();
486 TRACE("close id %d handle %d", id, handle);
487 ret = handle_close(handle);
488 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
489 send_status(id, status);
497 int handle, fd, ret, status = SSH2_FX_FAILURE;
501 handle = get_handle();
505 TRACE("read id %d handle %d off %lld len %d", id, handle, off, len);
506 if (len > sizeof buf) {
508 log("read change len %d", len);
510 fd = handle_to_fd(handle);
512 if (lseek(fd, off, SEEK_SET) < 0) {
513 error("process_read: seek failed");
514 status = errno_to_portable(errno);
516 ret = read(fd, buf, len);
518 status = errno_to_portable(errno);
519 } else if (ret == 0) {
520 status = SSH2_FX_EOF;
522 send_data(id, buf, ret);
527 if (status != SSH2_FX_OK)
528 send_status(id, status);
537 int handle, fd, ret, status = SSH2_FX_FAILURE;
541 handle = get_handle();
543 data = get_string(&len);
545 TRACE("write id %d handle %d off %lld len %d", id, handle, off, len);
546 fd = handle_to_fd(handle);
548 if (lseek(fd, off, SEEK_SET) < 0) {
549 status = errno_to_portable(errno);
550 error("process_write: seek failed");
553 ret = write(fd, data, len);
555 error("process_write: write failed");
556 status = errno_to_portable(errno);
557 } else if (ret == len) {
560 log("nothing at all written");
564 send_status(id, status);
569 process_do_stat(int do_lstat)
575 int ret, status = SSH2_FX_FAILURE;
578 name = get_string(NULL);
579 TRACE("%sstat id %d name %s", do_lstat ? "l" : "", id, name);
580 ret = do_lstat ? lstat(name, &st) : stat(name, &st);
582 status = errno_to_portable(errno);
584 a = stat_to_attrib(&st);
588 if (status != SSH2_FX_OK)
589 send_status(id, status);
611 int fd, ret, handle, status = SSH2_FX_FAILURE;
614 handle = get_handle();
615 TRACE("fstat id %d handle %d", id, handle);
616 fd = handle_to_fd(handle);
618 ret = fstat(fd, &st);
620 status = errno_to_portable(errno);
622 a = stat_to_attrib(&st);
627 if (status != SSH2_FX_OK)
628 send_status(id, status);
632 attrib_to_tv(Attrib *a)
634 static struct timeval tv[2];
635 tv[0].tv_sec = a->atime;
637 tv[1].tv_sec = a->mtime;
643 process_setstat(void)
649 int status = SSH2_FX_OK;
652 name = get_string(NULL);
654 TRACE("setstat id %d name %s", id, name);
655 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
656 ret = chmod(name, a->perm & 0777);
658 status = errno_to_portable(errno);
660 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
661 ret = utimes(name, attrib_to_tv(a));
663 status = errno_to_portable(errno);
665 send_status(id, status);
670 process_fsetstat(void)
675 int status = SSH2_FX_OK;
679 handle = get_handle();
681 TRACE("fsetstat id %d handle %d", id, handle);
682 fd = handle_to_fd(handle);
683 name = handle_to_name(handle);
684 if ((fd < 0) || (name == NULL)) {
685 status = SSH2_FX_FAILURE;
687 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
689 ret = fchmod(fd, a->perm & 0777);
691 ret = chmod(name, a->perm & 077);
694 status = errno_to_portable(errno);
696 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
698 ret = futimes(fd, attrib_to_tv(a));
700 ret = utimes(name, attrib_to_tv(a));
703 status = errno_to_portable(errno);
706 send_status(id, status);
710 process_opendir(void)
714 int handle, status = SSH2_FX_FAILURE;
718 path = get_string(NULL);
719 TRACE("opendir id %d path %s", id, path);
720 dirp = opendir(path);
722 status = errno_to_portable(errno);
724 handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp);
728 send_handle(id, handle);
733 if (status != SSH2_FX_OK)
734 send_status(id, status);
739 * XXX, draft-ietf-secsh-filexfer-00.txt says:
740 * The recommended format for the longname field is as follows:
741 * -rwxr-xr-x 1 mjos staff 348911 Mar 25 14:29 t-filexfer
742 * 1234567890 123 12345678 12345678 12345678 123456789012
745 ls_file(char *name, struct stat *st)
748 snprintf(buf, sizeof buf, "0%o %d %d %lld %d %s",
749 st->st_mode, st->st_uid, st->st_gid, (long long)st->st_size,
750 (int)st->st_mtime, name);
755 process_readdir(void)
764 handle = get_handle();
765 TRACE("readdir id %d handle %d", id, handle);
766 dirp = handle_to_dir(handle);
767 path = handle_to_name(handle);
768 if (dirp == NULL || path == NULL) {
769 send_status(id, SSH2_FX_FAILURE);
775 int nstats = 10, count = 0, i;
776 stats = xmalloc(nstats * sizeof(Stat));
777 while ((dp = readdir(dirp)) != NULL) {
778 if (count >= nstats) {
780 stats = xrealloc(stats, nstats * sizeof(Stat));
783 snprintf(pathname, sizeof pathname,
784 "%s/%s", path, dp->d_name);
785 if (lstat(pathname, &st) < 0)
787 a = stat_to_attrib(&st);
788 stats[count].attrib = *a;
789 stats[count].name = xstrdup(dp->d_name);
790 stats[count].long_name = ls_file(dp->d_name, &st);
792 /* send up to 100 entries in one message */
797 send_names(id, count, stats);
798 for(i = 0; i < count; i++) {
799 xfree(stats[i].name);
800 xfree(stats[i].long_name);
803 send_status(id, SSH2_FX_EOF);
814 int status = SSH2_FX_FAILURE;
818 name = get_string(NULL);
819 TRACE("remove id %d name %s", id, name);
821 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
822 send_status(id, status);
832 int ret, mode, status = SSH2_FX_FAILURE;
835 name = get_string(NULL);
837 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
838 a->perm & 0777 : 0777;
839 TRACE("mkdir id %d name %s mode 0%o", id, name, mode);
840 ret = mkdir(name, mode);
841 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
842 send_status(id, status);
854 name = get_string(NULL);
855 TRACE("rmdir id %d name %s", id, name);
857 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
858 send_status(id, status);
863 process_realpath(void)
865 char resolvedname[MAXPATHLEN];
870 path = get_string(NULL);
871 if (path[0] == '\0') {
875 TRACE("realpath id %d path %s", id, path);
876 if (realpath(path, resolvedname) == NULL) {
877 send_status(id, errno_to_portable(errno));
880 attrib_clear(&s.attrib);
881 s.name = s.long_name = resolvedname;
882 send_names(id, 1, &s);
891 char *oldpath, *newpath;
895 oldpath = get_string(NULL);
896 newpath = get_string(NULL);
897 TRACE("rename id %d old %s new %s", id, oldpath, newpath);
898 ret = rename(oldpath, newpath);
899 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
900 send_status(id, status);
906 process_extended(void)
912 request = get_string(NULL);
913 send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
917 /* stolen from ssh-agent */
926 if (buffer_len(&iqueue) < 5)
927 return; /* Incomplete message. */
928 cp = (u_char *) buffer_ptr(&iqueue);
929 msg_len = GET_32BIT(cp);
930 if (msg_len > 256 * 1024) {
931 error("bad message ");
934 if (buffer_len(&iqueue) < msg_len + 4)
936 buffer_consume(&iqueue, 4);
937 type = buffer_get_char(&iqueue);
960 case SSH2_FXP_SETSTAT:
963 case SSH2_FXP_FSETSTAT:
966 case SSH2_FXP_OPENDIR:
969 case SSH2_FXP_READDIR:
972 case SSH2_FXP_REMOVE:
981 case SSH2_FXP_REALPATH:
987 case SSH2_FXP_RENAME:
990 case SSH2_FXP_EXTENDED:
994 error("Unknown message %d", type);
1000 main(int ac, char **av)
1006 __progname = get_progname(av[0]);
1009 log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
1011 in = dup(STDIN_FILENO);
1012 out = dup(STDOUT_FILENO);
1020 buffer_init(&iqueue);
1021 buffer_init(&oqueue);
1028 olen = buffer_len(&oqueue);
1032 if (select(max+1, &rset, &wset, NULL, NULL) < 0) {
1038 /* copy stdin to iqueue */
1039 if (FD_ISSET(in, &rset)) {
1041 len = read(in, buf, sizeof buf);
1045 } else if (len < 0) {
1046 error("read error");
1049 buffer_append(&iqueue, buf, len);
1052 /* send oqueue to stdout */
1053 if (FD_ISSET(out, &wset)) {
1054 len = write(out, buffer_ptr(&oqueue), olen);
1056 error("write error");
1059 buffer_consume(&oqueue, len);
1062 /* process requests from client */