]> andersk Git - openssh.git/blob - sftp-server.c
- millert@cvs.openbsd.org 2001/03/04 17:42:28
[openssh.git] / sftp-server.c
1 /*
2  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
23  */
24 #include "includes.h"
25 RCSID("$OpenBSD: sftp-server.c,v 1.22 2001/03/03 22:07:50 deraadt Exp $");
26
27 #include "buffer.h"
28 #include "bufaux.h"
29 #include "getput.h"
30 #include "log.h"
31 #include "xmalloc.h"
32
33 #include "sftp.h"
34 #include "sftp-common.h"
35
36 /* helper */
37 #define get_int64()                     buffer_get_int64(&iqueue);
38 #define get_int()                       buffer_get_int(&iqueue);
39 #define get_string(lenp)                buffer_get_string(&iqueue, lenp);
40 #define TRACE                           debug
41
42 #ifdef HAVE___PROGNAME
43 extern char *__progname;
44 #else
45 char *__progname;
46 #endif
47
48 /* input and output queue */
49 Buffer iqueue;
50 Buffer oqueue;
51
52 /* portable attibutes, etc. */
53
54 typedef struct Stat Stat;
55
56 struct Stat {
57         char *name;
58         char *long_name;
59         Attrib attrib;
60 };
61
62 int
63 errno_to_portable(int unixerrno)
64 {
65         int ret = 0;
66
67         switch (unixerrno) {
68         case 0:
69                 ret = SSH2_FX_OK;
70                 break;
71         case ENOENT:
72         case ENOTDIR:
73         case EBADF:
74         case ELOOP:
75                 ret = SSH2_FX_NO_SUCH_FILE;
76                 break;
77         case EPERM:
78         case EACCES:
79         case EFAULT:
80                 ret = SSH2_FX_PERMISSION_DENIED;
81                 break;
82         case ENAMETOOLONG:
83         case EINVAL:
84                 ret = SSH2_FX_BAD_MESSAGE;
85                 break;
86         default:
87                 ret = SSH2_FX_FAILURE;
88                 break;
89         }
90         return ret;
91 }
92
93 int
94 flags_from_portable(int pflags)
95 {
96         int flags = 0;
97
98         if ((pflags & SSH2_FXF_READ) &&
99             (pflags & SSH2_FXF_WRITE)) {
100                 flags = O_RDWR;
101         } else if (pflags & SSH2_FXF_READ) {
102                 flags = O_RDONLY;
103         } else if (pflags & SSH2_FXF_WRITE) {
104                 flags = O_WRONLY;
105         }
106         if (pflags & SSH2_FXF_CREAT)
107                 flags |= O_CREAT;
108         if (pflags & SSH2_FXF_TRUNC)
109                 flags |= O_TRUNC;
110         if (pflags & SSH2_FXF_EXCL)
111                 flags |= O_EXCL;
112         return flags;
113 }
114
115 Attrib *
116 get_attrib(void)
117 {
118         return decode_attrib(&iqueue);
119 }
120
121 /* handle handles */
122
123 typedef struct Handle Handle;
124 struct Handle {
125         int use;
126         DIR *dirp;
127         int fd;
128         char *name;
129 };
130
131 enum {
132         HANDLE_UNUSED,
133         HANDLE_DIR,
134         HANDLE_FILE
135 };
136
137 Handle  handles[100];
138
139 void
140 handle_init(void)
141 {
142         int i;
143
144         for(i = 0; i < sizeof(handles)/sizeof(Handle); i++)
145                 handles[i].use = HANDLE_UNUSED;
146 }
147
148 int
149 handle_new(int use, char *name, int fd, DIR *dirp)
150 {
151         int i;
152
153         for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
154                 if (handles[i].use == HANDLE_UNUSED) {
155                         handles[i].use = use;
156                         handles[i].dirp = dirp;
157                         handles[i].fd = fd;
158                         handles[i].name = name;
159                         return i;
160                 }
161         }
162         return -1;
163 }
164
165 int
166 handle_is_ok(int i, int type)
167 {
168         return i >= 0 && i < sizeof(handles)/sizeof(Handle) &&
169             handles[i].use == type;
170 }
171
172 int
173 handle_to_string(int handle, char **stringp, int *hlenp)
174 {
175         if (stringp == NULL || hlenp == NULL)
176                 return -1;
177         *stringp = xmalloc(sizeof(int32_t));
178         PUT_32BIT(*stringp, handle);
179         *hlenp = sizeof(int32_t);
180         return 0;
181 }
182
183 int
184 handle_from_string(char *handle, u_int hlen)
185 {
186         int val;
187
188         if (hlen != sizeof(int32_t))
189                 return -1;
190         val = GET_32BIT(handle);
191         if (handle_is_ok(val, HANDLE_FILE) ||
192             handle_is_ok(val, HANDLE_DIR))
193                 return val;
194         return -1;
195 }
196
197 char *
198 handle_to_name(int handle)
199 {
200         if (handle_is_ok(handle, HANDLE_DIR)||
201             handle_is_ok(handle, HANDLE_FILE))
202                 return handles[handle].name;
203         return NULL;
204 }
205
206 DIR *
207 handle_to_dir(int handle)
208 {
209         if (handle_is_ok(handle, HANDLE_DIR))
210                 return handles[handle].dirp;
211         return NULL;
212 }
213
214 int
215 handle_to_fd(int handle)
216 {
217         if (handle_is_ok(handle, HANDLE_FILE))
218                 return handles[handle].fd;
219         return -1;
220 }
221
222 int
223 handle_close(int handle)
224 {
225         int ret = -1;
226
227         if (handle_is_ok(handle, HANDLE_FILE)) {
228                 ret = close(handles[handle].fd);
229                 handles[handle].use = HANDLE_UNUSED;
230         } else if (handle_is_ok(handle, HANDLE_DIR)) {
231                 ret = closedir(handles[handle].dirp);
232                 handles[handle].use = HANDLE_UNUSED;
233         } else {
234                 errno = ENOENT;
235         }
236         return ret;
237 }
238
239 int
240 get_handle(void)
241 {
242         char *handle;
243         int val = -1;
244         u_int hlen;
245
246         handle = get_string(&hlen);
247         if (hlen < 256)
248                 val = handle_from_string(handle, hlen);
249         xfree(handle);
250         return val;
251 }
252
253 /* send replies */
254
255 void
256 send_msg(Buffer *m)
257 {
258         int mlen = buffer_len(m);
259
260         buffer_put_int(&oqueue, mlen);
261         buffer_append(&oqueue, buffer_ptr(m), mlen);
262         buffer_consume(m, mlen);
263 }
264
265 void
266 send_status(u_int32_t id, u_int32_t error)
267 {
268         Buffer msg;
269
270         TRACE("sent status id %d error %d", id, error);
271         buffer_init(&msg);
272         buffer_put_char(&msg, SSH2_FXP_STATUS);
273         buffer_put_int(&msg, id);
274         buffer_put_int(&msg, error);
275         send_msg(&msg);
276         buffer_free(&msg);
277 }
278 void
279 send_data_or_handle(char type, u_int32_t id, char *data, int dlen)
280 {
281         Buffer msg;
282
283         buffer_init(&msg);
284         buffer_put_char(&msg, type);
285         buffer_put_int(&msg, id);
286         buffer_put_string(&msg, data, dlen);
287         send_msg(&msg);
288         buffer_free(&msg);
289 }
290
291 void
292 send_data(u_int32_t id, char *data, int dlen)
293 {
294         TRACE("sent data id %d len %d", id, dlen);
295         send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
296 }
297
298 void
299 send_handle(u_int32_t id, int handle)
300 {
301         char *string;
302         int hlen;
303
304         handle_to_string(handle, &string, &hlen);
305         TRACE("sent handle id %d handle %d", id, handle);
306         send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
307         xfree(string);
308 }
309
310 void
311 send_names(u_int32_t id, int count, Stat *stats)
312 {
313         Buffer msg;
314         int i;
315
316         buffer_init(&msg);
317         buffer_put_char(&msg, SSH2_FXP_NAME);
318         buffer_put_int(&msg, id);
319         buffer_put_int(&msg, count);
320         TRACE("sent names id %d count %d", id, count);
321         for (i = 0; i < count; i++) {
322                 buffer_put_cstring(&msg, stats[i].name);
323                 buffer_put_cstring(&msg, stats[i].long_name);
324                 encode_attrib(&msg, &stats[i].attrib);
325         }
326         send_msg(&msg);
327         buffer_free(&msg);
328 }
329
330 void
331 send_attrib(u_int32_t id, Attrib *a)
332 {
333         Buffer msg;
334
335         TRACE("sent attrib id %d have 0x%x", id, a->flags);
336         buffer_init(&msg);
337         buffer_put_char(&msg, SSH2_FXP_ATTRS);
338         buffer_put_int(&msg, id);
339         encode_attrib(&msg, a);
340         send_msg(&msg);
341         buffer_free(&msg);
342 }
343
344 /* parse incoming */
345
346 void
347 process_init(void)
348 {
349         Buffer msg;
350         int version = buffer_get_int(&iqueue);
351
352         TRACE("client version %d", version);
353         buffer_init(&msg);
354         buffer_put_char(&msg, SSH2_FXP_VERSION);
355         buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
356         send_msg(&msg);
357         buffer_free(&msg);
358 }
359
360 void
361 process_open(void)
362 {
363         u_int32_t id, pflags;
364         Attrib *a;
365         char *name;
366         int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
367
368         id = get_int();
369         name = get_string(NULL);
370         pflags = get_int();             /* portable flags */
371         a = get_attrib();
372         flags = flags_from_portable(pflags);
373         mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
374         TRACE("open id %d name %s flags %d mode 0%o", id, name, pflags, mode);
375         fd = open(name, flags, mode);
376         if (fd < 0) {
377                 status = errno_to_portable(errno);
378         } else {
379                 handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL);
380                 if (handle < 0) {
381                         close(fd);
382                 } else {
383                         send_handle(id, handle);
384                         status = SSH2_FX_OK;
385                 }
386         }
387         if (status != SSH2_FX_OK)
388                 send_status(id, status);
389         xfree(name);
390 }
391
392 void
393 process_close(void)
394 {
395         u_int32_t id;
396         int handle, ret, status = SSH2_FX_FAILURE;
397
398         id = get_int();
399         handle = get_handle();
400         TRACE("close id %d handle %d", id, handle);
401         ret = handle_close(handle);
402         status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
403         send_status(id, status);
404 }
405
406 void
407 process_read(void)
408 {
409         char buf[64*1024];
410         u_int32_t id, len;
411         int handle, fd, ret, status = SSH2_FX_FAILURE;
412         u_int64_t off;
413
414         id = get_int();
415         handle = get_handle();
416         off = get_int64();
417         len = get_int();
418
419         TRACE("read id %d handle %d off %llu len %d", id, handle,
420             (u_int64_t)off, len);
421         if (len > sizeof buf) {
422                 len = sizeof buf;
423                 log("read change len %d", len);
424         }
425         fd = handle_to_fd(handle);
426         if (fd >= 0) {
427                 if (lseek(fd, off, SEEK_SET) < 0) {
428                         error("process_read: seek failed");
429                         status = errno_to_portable(errno);
430                 } else {
431                         ret = read(fd, buf, len);
432                         if (ret < 0) {
433                                 status = errno_to_portable(errno);
434                         } else if (ret == 0) {
435                                 status = SSH2_FX_EOF;
436                         } else {
437                                 send_data(id, buf, ret);
438                                 status = SSH2_FX_OK;
439                         }
440                 }
441         }
442         if (status != SSH2_FX_OK)
443                 send_status(id, status);
444 }
445
446 void
447 process_write(void)
448 {
449         u_int32_t id;
450         u_int64_t off;
451         u_int len;
452         int handle, fd, ret, status = SSH2_FX_FAILURE;
453         char *data;
454
455         id = get_int();
456         handle = get_handle();
457         off = get_int64();
458         data = get_string(&len);
459
460         TRACE("write id %d handle %d off %llu len %d", id, handle,
461             (u_int64_t)off, len);
462         fd = handle_to_fd(handle);
463         if (fd >= 0) {
464                 if (lseek(fd, off, SEEK_SET) < 0) {
465                         status = errno_to_portable(errno);
466                         error("process_write: seek failed");
467                 } else {
468 /* XXX ATOMICIO ? */
469                         ret = write(fd, data, len);
470                         if (ret == -1) {
471                                 error("process_write: write failed");
472                                 status = errno_to_portable(errno);
473                         } else if (ret == len) {
474                                 status = SSH2_FX_OK;
475                         } else {
476                                 log("nothing at all written");
477                         }
478                 }
479         }
480         send_status(id, status);
481         xfree(data);
482 }
483
484 void
485 process_do_stat(int do_lstat)
486 {
487         Attrib a;
488         struct stat st;
489         u_int32_t id;
490         char *name;
491         int ret, status = SSH2_FX_FAILURE;
492
493         id = get_int();
494         name = get_string(NULL);
495         TRACE("%sstat id %d name %s", do_lstat ? "l" : "", id, name);
496         ret = do_lstat ? lstat(name, &st) : stat(name, &st);
497         if (ret < 0) {
498                 status = errno_to_portable(errno);
499         } else {
500                 stat_to_attrib(&st, &a);
501                 send_attrib(id, &a);
502                 status = SSH2_FX_OK;
503         }
504         if (status != SSH2_FX_OK)
505                 send_status(id, status);
506         xfree(name);
507 }
508
509 void
510 process_stat(void)
511 {
512         process_do_stat(0);
513 }
514
515 void
516 process_lstat(void)
517 {
518         process_do_stat(1);
519 }
520
521 void
522 process_fstat(void)
523 {
524         Attrib a;
525         struct stat st;
526         u_int32_t id;
527         int fd, ret, handle, status = SSH2_FX_FAILURE;
528
529         id = get_int();
530         handle = get_handle();
531         TRACE("fstat id %d handle %d", id, handle);
532         fd = handle_to_fd(handle);
533         if (fd  >= 0) {
534                 ret = fstat(fd, &st);
535                 if (ret < 0) {
536                         status = errno_to_portable(errno);
537                 } else {
538                         stat_to_attrib(&st, &a);
539                         send_attrib(id, &a);
540                         status = SSH2_FX_OK;
541                 }
542         }
543         if (status != SSH2_FX_OK)
544                 send_status(id, status);
545 }
546
547 struct timeval *
548 attrib_to_tv(Attrib *a)
549 {
550         static struct timeval tv[2];
551
552         tv[0].tv_sec = a->atime;
553         tv[0].tv_usec = 0;
554         tv[1].tv_sec = a->mtime;
555         tv[1].tv_usec = 0;
556         return tv;
557 }
558
559 void
560 process_setstat(void)
561 {
562         Attrib *a;
563         u_int32_t id;
564         char *name;
565         int ret;
566         int status = SSH2_FX_OK;
567
568         id = get_int();
569         name = get_string(NULL);
570         a = get_attrib();
571         TRACE("setstat id %d name %s", id, name);
572         if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
573                 ret = chmod(name, a->perm & 0777);
574                 if (ret == -1)
575                         status = errno_to_portable(errno);
576         }
577         if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
578                 ret = utimes(name, attrib_to_tv(a));
579                 if (ret == -1)
580                         status = errno_to_portable(errno);
581         }
582         if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
583                 ret = chown(name, a->uid, a->gid);
584                 if (ret == -1)
585                         status = errno_to_portable(errno);
586         }
587         send_status(id, status);
588         xfree(name);
589 }
590
591 void
592 process_fsetstat(void)
593 {
594         Attrib *a;
595         u_int32_t id;
596         int handle, fd, ret;
597         int status = SSH2_FX_OK;
598         char *name;
599
600         id = get_int();
601         handle = get_handle();
602         a = get_attrib();
603         TRACE("fsetstat id %d handle %d", id, handle);
604         fd = handle_to_fd(handle);
605         name = handle_to_name(handle);
606         if (fd < 0 || name == NULL) {
607                 status = SSH2_FX_FAILURE;
608         } else {
609                 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
610 #ifdef HAVE_FCHMOD
611                         ret = fchmod(fd, a->perm & 0777);
612 #else
613                         ret = chmod(name, a->perm & 0777);
614 #endif
615                         if (ret == -1)
616                                 status = errno_to_portable(errno);
617                 }
618                 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
619 #ifdef HAVE_FUTIMES
620                         ret = futimes(fd, attrib_to_tv(a));
621 #else
622                         ret = utimes(name, attrib_to_tv(a));
623 #endif
624                         if (ret == -1)
625                                 status = errno_to_portable(errno);
626                 }
627                 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
628 #ifdef HAVE_FCHOWN
629                         ret = fchown(fd, a->uid, a->gid);
630 #else
631                         ret = chown(name, a->uid, a->gid);
632 #endif
633                         if (ret == -1)
634                                 status = errno_to_portable(errno);
635                 }
636         }
637         send_status(id, status);
638 }
639
640 void
641 process_opendir(void)
642 {
643         DIR *dirp = NULL;
644         char *path;
645         int handle, status = SSH2_FX_FAILURE;
646         u_int32_t id;
647
648         id = get_int();
649         path = get_string(NULL);
650         TRACE("opendir id %d path %s", id, path);
651         dirp = opendir(path);
652         if (dirp == NULL) {
653                 status = errno_to_portable(errno);
654         } else {
655                 handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp);
656                 if (handle < 0) {
657                         closedir(dirp);
658                 } else {
659                         send_handle(id, handle);
660                         status = SSH2_FX_OK;
661                 }
662
663         }
664         if (status != SSH2_FX_OK)
665                 send_status(id, status);
666         xfree(path);
667 }
668
669 /*
670  * drwxr-xr-x    5 markus   markus       1024 Jan 13 18:39 .ssh
671  */
672 char *
673 ls_file(char *name, struct stat *st)
674 {
675         int sz = 0;
676         struct passwd *pw;
677         struct group *gr;
678         struct tm *ltime = localtime(&st->st_mtime);
679         char *user, *group;
680         char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1];
681
682         strmode(st->st_mode, mode);
683         if ((pw = getpwuid(st->st_uid)) != NULL) {
684                 user = pw->pw_name;
685         } else {
686                 snprintf(ubuf, sizeof ubuf, "%d", st->st_uid);
687                 user = ubuf;
688         }
689         if ((gr = getgrgid(st->st_gid)) != NULL) {
690                 group = gr->gr_name;
691         } else {
692                 snprintf(gbuf, sizeof gbuf, "%d", st->st_gid);
693                 group = gbuf;
694         }
695         if (ltime != NULL) {
696                 if (time(NULL) - st->st_mtime < (365*24*60*60)/2)
697                         sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime);
698                 else
699                         sz = strftime(tbuf, sizeof tbuf, "%b %e  %Y", ltime);
700         }
701         if (sz == 0)
702                 tbuf[0] = '\0';
703         snprintf(buf, sizeof buf, "%s %3d %-8.8s %-8.8s %8llu %s %s", mode,
704             st->st_nlink, user, group, (u_int64_t)st->st_size, tbuf, name);
705         return xstrdup(buf);
706 }
707
708 void
709 process_readdir(void)
710 {
711         DIR *dirp;
712         struct dirent *dp;
713         char *path;
714         int handle;
715         u_int32_t id;
716
717         id = get_int();
718         handle = get_handle();
719         TRACE("readdir id %d handle %d", id, handle);
720         dirp = handle_to_dir(handle);
721         path = handle_to_name(handle);
722         if (dirp == NULL || path == NULL) {
723                 send_status(id, SSH2_FX_FAILURE);
724         } else {
725                 struct stat st;
726                 char pathname[1024];
727                 Stat *stats;
728                 int nstats = 10, count = 0, i;
729                 stats = xmalloc(nstats * sizeof(Stat));
730                 while ((dp = readdir(dirp)) != NULL) {
731                         if (count >= nstats) {
732                                 nstats *= 2;
733                                 stats = xrealloc(stats, nstats * sizeof(Stat));
734                         }
735 /* XXX OVERFLOW ? */
736                         snprintf(pathname, sizeof pathname,
737                             "%s/%s", path, dp->d_name);
738                         if (lstat(pathname, &st) < 0)
739                                 continue;
740                         stat_to_attrib(&st, &(stats[count].attrib));
741                         stats[count].name = xstrdup(dp->d_name);
742                         stats[count].long_name = ls_file(dp->d_name, &st);
743                         count++;
744                         /* send up to 100 entries in one message */
745                         /* XXX check packet size instead */
746                         if (count == 100)
747                                 break;
748                 }
749                 if (count > 0) {
750                         send_names(id, count, stats);
751                         for(i = 0; i < count; i++) {
752                                 xfree(stats[i].name);
753                                 xfree(stats[i].long_name);
754                         }
755                 } else {
756                         send_status(id, SSH2_FX_EOF);
757                 }
758                 xfree(stats);
759         }
760 }
761
762 void
763 process_remove(void)
764 {
765         char *name;
766         u_int32_t id;
767         int status = SSH2_FX_FAILURE;
768         int ret;
769
770         id = get_int();
771         name = get_string(NULL);
772         TRACE("remove id %d name %s", id, name);
773         ret = unlink(name);
774         status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
775         send_status(id, status);
776         xfree(name);
777 }
778
779 void
780 process_mkdir(void)
781 {
782         Attrib *a;
783         u_int32_t id;
784         char *name;
785         int ret, mode, status = SSH2_FX_FAILURE;
786
787         id = get_int();
788         name = get_string(NULL);
789         a = get_attrib();
790         mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
791             a->perm & 0777 : 0777;
792         TRACE("mkdir id %d name %s mode 0%o", id, name, mode);
793         ret = mkdir(name, mode);
794         status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
795         send_status(id, status);
796         xfree(name);
797 }
798
799 void
800 process_rmdir(void)
801 {
802         u_int32_t id;
803         char *name;
804         int ret, status;
805
806         id = get_int();
807         name = get_string(NULL);
808         TRACE("rmdir id %d name %s", id, name);
809         ret = rmdir(name);
810         status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
811         send_status(id, status);
812         xfree(name);
813 }
814
815 void
816 process_realpath(void)
817 {
818         char resolvedname[MAXPATHLEN];
819         u_int32_t id;
820         char *path;
821
822         id = get_int();
823         path = get_string(NULL);
824         if (path[0] == '\0') {
825                 xfree(path);
826                 path = xstrdup(".");
827         }
828         TRACE("realpath id %d path %s", id, path);
829         if (realpath(path, resolvedname) == NULL) {
830                 send_status(id, errno_to_portable(errno));
831         } else {
832                 Stat s;
833                 attrib_clear(&s.attrib);
834                 s.name = s.long_name = resolvedname;
835                 send_names(id, 1, &s);
836         }
837         xfree(path);
838 }
839
840 void
841 process_rename(void)
842 {
843         u_int32_t id;
844         struct stat st;
845         char *oldpath, *newpath;
846         int ret, status = SSH2_FX_FAILURE;
847
848         id = get_int();
849         oldpath = get_string(NULL);
850         newpath = get_string(NULL);
851         TRACE("rename id %d old %s new %s", id, oldpath, newpath);
852         /* fail if 'newpath' exists */
853         if (stat(newpath, &st) == -1) {
854                 ret = rename(oldpath, newpath);
855                 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
856         }
857         send_status(id, status);
858         xfree(oldpath);
859         xfree(newpath);
860 }
861
862 void
863 process_extended(void)
864 {
865         u_int32_t id;
866         char *request;
867
868         id = get_int();
869         request = get_string(NULL);
870         send_status(id, SSH2_FX_OP_UNSUPPORTED);                /* MUST */
871         xfree(request);
872 }
873
874 /* stolen from ssh-agent */
875
876 void
877 process(void)
878 {
879         u_int msg_len;
880         u_int type;
881         u_char *cp;
882
883         if (buffer_len(&iqueue) < 5)
884                 return;         /* Incomplete message. */
885         cp = (u_char *) buffer_ptr(&iqueue);
886         msg_len = GET_32BIT(cp);
887         if (msg_len > 256 * 1024) {
888                 error("bad message ");
889                 exit(11);
890         }
891         if (buffer_len(&iqueue) < msg_len + 4)
892                 return;
893         buffer_consume(&iqueue, 4);
894         type = buffer_get_char(&iqueue);
895         switch (type) {
896         case SSH2_FXP_INIT:
897                 process_init();
898                 break;
899         case SSH2_FXP_OPEN:
900                 process_open();
901                 break;
902         case SSH2_FXP_CLOSE:
903                 process_close();
904                 break;
905         case SSH2_FXP_READ:
906                 process_read();
907                 break;
908         case SSH2_FXP_WRITE:
909                 process_write();
910                 break;
911         case SSH2_FXP_LSTAT:
912                 process_lstat();
913                 break;
914         case SSH2_FXP_FSTAT:
915                 process_fstat();
916                 break;
917         case SSH2_FXP_SETSTAT:
918                 process_setstat();
919                 break;
920         case SSH2_FXP_FSETSTAT:
921                 process_fsetstat();
922                 break;
923         case SSH2_FXP_OPENDIR:
924                 process_opendir();
925                 break;
926         case SSH2_FXP_READDIR:
927                 process_readdir();
928                 break;
929         case SSH2_FXP_REMOVE:
930                 process_remove();
931                 break;
932         case SSH2_FXP_MKDIR:
933                 process_mkdir();
934                 break;
935         case SSH2_FXP_RMDIR:
936                 process_rmdir();
937                 break;
938         case SSH2_FXP_REALPATH:
939                 process_realpath();
940                 break;
941         case SSH2_FXP_STAT:
942                 process_stat();
943                 break;
944         case SSH2_FXP_RENAME:
945                 process_rename();
946                 break;
947         case SSH2_FXP_EXTENDED:
948                 process_extended();
949                 break;
950         default:
951                 error("Unknown message %d", type);
952                 break;
953         }
954 }
955
956 int
957 main(int ac, char **av)
958 {
959         fd_set *rset, *wset;
960         int in, out, max;
961         ssize_t len, olen, set_size;
962
963         __progname = get_progname(av[0]);
964         handle_init();
965
966 #ifdef DEBUG_SFTP_SERVER
967         log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
968 #endif
969
970         in = dup(STDIN_FILENO);
971         out = dup(STDOUT_FILENO);
972
973         max = 0;
974         if (in > max)
975                 max = in;
976         if (out > max)
977                 max = out;
978
979         buffer_init(&iqueue);
980         buffer_init(&oqueue);
981
982         set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
983         rset = (fd_set *)xmalloc(set_size);
984         wset = (fd_set *)xmalloc(set_size);
985
986         for (;;) {
987                 memset(rset, 0, set_size);
988                 memset(wset, 0, set_size);
989
990                 FD_SET(in, rset);
991                 olen = buffer_len(&oqueue);
992                 if (olen > 0)
993                         FD_SET(out, wset);
994
995                 if (select(max+1, rset, wset, NULL, NULL) < 0) {
996                         if (errno == EINTR)
997                                 continue;
998                         exit(2);
999                 }
1000
1001                 /* copy stdin to iqueue */
1002                 if (FD_ISSET(in, rset)) {
1003                         char buf[4*4096];
1004                         len = read(in, buf, sizeof buf);
1005                         if (len == 0) {
1006                                 debug("read eof");
1007                                 exit(0);
1008                         } else if (len < 0) {
1009                                 error("read error");
1010                                 exit(1);
1011                         } else {
1012                                 buffer_append(&iqueue, buf, len);
1013                         }
1014                 }
1015                 /* send oqueue to stdout */
1016                 if (FD_ISSET(out, wset)) {
1017                         len = write(out, buffer_ptr(&oqueue), olen);
1018                         if (len < 0) {
1019                                 error("write error");
1020                                 exit(1);
1021                         } else {
1022                                 buffer_consume(&oqueue, len);
1023                         }
1024                 }
1025                 /* process requests from client */
1026                 process();
1027         }
1028 }
This page took 0.116448 seconds and 5 git commands to generate.