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