]> andersk Git - openssh.git/blob - sftp-server.c
- (stevesk) sftp-server.c: KNF
[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.14 2001/01/21 19:05:56 markus 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
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 void
193 stat_to_attrib(struct stat *st, Attrib *a)
194 {
195         attrib_clear(a);
196         a->flags = 0;
197         a->flags |= SSH2_FILEXFER_ATTR_SIZE;
198         a->size = st->st_size;
199         a->flags |= SSH2_FILEXFER_ATTR_UIDGID;
200         a->uid = st->st_uid;
201         a->gid = st->st_gid;
202         a->flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
203         a->perm = st->st_mode;
204         a->flags |= SSH2_FILEXFER_ATTR_ACMODTIME;
205         a->atime = st->st_atime;
206         a->mtime = st->st_mtime;
207 }
208
209 Attrib *
210 get_attrib(void)
211 {
212         return decode_attrib(&iqueue);
213 }
214
215 /* handle handles */
216
217 typedef struct Handle Handle;
218 struct Handle {
219         int use;
220         DIR *dirp;
221         int fd;
222         char *name;
223 };
224 enum {
225         HANDLE_UNUSED,
226         HANDLE_DIR,
227         HANDLE_FILE
228 };
229 Handle  handles[100];
230
231 void
232 handle_init(void)
233 {
234         int i;
235         for(i = 0; i < sizeof(handles)/sizeof(Handle); i++)
236                 handles[i].use = HANDLE_UNUSED;
237 }
238
239 int
240 handle_new(int use, char *name, int fd, DIR *dirp)
241 {
242         int i;
243         for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
244                 if (handles[i].use == HANDLE_UNUSED) {
245                         handles[i].use = use;
246                         handles[i].dirp = dirp;
247                         handles[i].fd = fd;
248                         handles[i].name = name;
249                         return i;
250                 }
251         }
252         return -1;
253 }
254
255 int
256 handle_is_ok(int i, int type)
257 {
258         return i >= 0 && i < sizeof(handles)/sizeof(Handle) &&
259             handles[i].use == type;
260 }
261
262 int
263 handle_to_string(int handle, char **stringp, int *hlenp)
264 {
265         if (stringp == NULL || hlenp == NULL)
266                 return -1;
267         *stringp = xmalloc(sizeof(int32_t));
268         PUT_32BIT(*stringp, handle);
269         *hlenp = sizeof(int32_t);
270         return 0;
271 }
272
273 int
274 handle_from_string(char *handle, u_int hlen)
275 {
276         int val;
277         if (hlen != sizeof(int32_t))
278                 return -1;
279         val = GET_32BIT(handle);
280         if (handle_is_ok(val, HANDLE_FILE) ||
281             handle_is_ok(val, HANDLE_DIR))
282                 return val;
283         return -1;
284 }
285
286 char *
287 handle_to_name(int handle)
288 {
289         if (handle_is_ok(handle, HANDLE_DIR)||
290             handle_is_ok(handle, HANDLE_FILE))
291                 return handles[handle].name;
292         return NULL;
293 }
294
295 DIR *
296 handle_to_dir(int handle)
297 {
298         if (handle_is_ok(handle, HANDLE_DIR))
299                 return handles[handle].dirp;
300         return NULL;
301 }
302
303 int
304 handle_to_fd(int handle)
305 {
306         if (handle_is_ok(handle, HANDLE_FILE)) 
307                 return handles[handle].fd;
308         return -1;
309 }
310
311 int
312 handle_close(int handle)
313 {
314         int ret = -1;
315         if (handle_is_ok(handle, HANDLE_FILE)) {
316                 ret = close(handles[handle].fd);
317                 handles[handle].use = HANDLE_UNUSED;
318         } else if (handle_is_ok(handle, HANDLE_DIR)) {
319                 ret = closedir(handles[handle].dirp);
320                 handles[handle].use = HANDLE_UNUSED;
321         } else {
322                 errno = ENOENT;
323         }
324         return ret;
325 }
326
327 int
328 get_handle(void)
329 {
330         char *handle;
331         int val = -1;
332         u_int hlen;
333         handle = get_string(&hlen);
334         if (hlen < 256)
335                 val = handle_from_string(handle, hlen);
336         xfree(handle);
337         return val;
338 }
339
340 /* send replies */
341
342 void
343 send_msg(Buffer *m)
344 {
345         int mlen = buffer_len(m);
346         buffer_put_int(&oqueue, mlen);
347         buffer_append(&oqueue, buffer_ptr(m), mlen);
348         buffer_consume(m, mlen);
349 }
350
351 void
352 send_status(u_int32_t id, u_int32_t error)
353 {
354         Buffer msg;
355         TRACE("sent status id %d error %d", id, error);
356         buffer_init(&msg);
357         buffer_put_char(&msg, SSH2_FXP_STATUS);
358         buffer_put_int(&msg, id);
359         buffer_put_int(&msg, error);
360         send_msg(&msg);
361         buffer_free(&msg);
362 }
363 void
364 send_data_or_handle(char type, u_int32_t id, char *data, int dlen)
365 {
366         Buffer msg;
367         buffer_init(&msg);
368         buffer_put_char(&msg, type);
369         buffer_put_int(&msg, id);
370         buffer_put_string(&msg, data, dlen);
371         send_msg(&msg);
372         buffer_free(&msg);
373 }
374
375 void
376 send_data(u_int32_t id, char *data, int dlen)
377 {
378         TRACE("sent data id %d len %d", id, dlen);
379         send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
380 }
381
382 void
383 send_handle(u_int32_t id, int handle)
384 {
385         char *string;
386         int hlen;
387         handle_to_string(handle, &string, &hlen);
388         TRACE("sent handle id %d handle %d", id, handle);
389         send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
390         xfree(string);
391 }
392
393 void
394 send_names(u_int32_t id, int count, Stat *stats)
395 {
396         Buffer msg;
397         int i;
398         buffer_init(&msg);
399         buffer_put_char(&msg, SSH2_FXP_NAME);
400         buffer_put_int(&msg, id);
401         buffer_put_int(&msg, count);
402         TRACE("sent names id %d count %d", id, count);
403         for (i = 0; i < count; i++) {
404                 buffer_put_cstring(&msg, stats[i].name);
405                 buffer_put_cstring(&msg, stats[i].long_name);
406                 encode_attrib(&msg, &stats[i].attrib);
407         }
408         send_msg(&msg);
409         buffer_free(&msg);
410 }
411
412 void
413 send_attrib(u_int32_t id, Attrib *a)
414 {
415         Buffer msg;
416         TRACE("sent attrib id %d have 0x%x", id, a->flags);
417         buffer_init(&msg);
418         buffer_put_char(&msg, SSH2_FXP_ATTRS);
419         buffer_put_int(&msg, id);
420         encode_attrib(&msg, a);
421         send_msg(&msg);
422         buffer_free(&msg);
423 }
424
425 /* parse incoming */
426
427 void
428 process_init(void)
429 {
430         Buffer msg;
431         int version = buffer_get_int(&iqueue);
432
433         TRACE("client version %d", version);
434         buffer_init(&msg);
435         buffer_put_char(&msg, SSH2_FXP_VERSION);
436         buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
437         send_msg(&msg);
438         buffer_free(&msg);
439 }
440
441 void
442 process_open(void)
443 {
444         u_int32_t id, pflags;
445         Attrib *a;
446         char *name;
447         int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
448
449         id = get_int();
450         name = get_string(NULL);
451         pflags = get_int();             /* portable flags */
452         a = get_attrib();
453         flags = flags_from_portable(pflags);
454         mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
455         TRACE("open id %d name %s flags %d mode 0%o", id, name, pflags, mode);
456         fd = open(name, flags, mode);
457         if (fd < 0) {
458                 status = errno_to_portable(errno);
459         } else {
460                 handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL);
461                 if (handle < 0) {
462                         close(fd);
463                 } else {
464                         send_handle(id, handle);
465                         status = SSH2_FX_OK;
466                 }
467         }
468         if (status != SSH2_FX_OK)
469                 send_status(id, status);
470         xfree(name);
471 }
472
473 void
474 process_close(void)
475 {
476         u_int32_t id;
477         int handle, ret, status = SSH2_FX_FAILURE;
478
479         id = get_int();
480         handle = get_handle();
481         TRACE("close id %d handle %d", id, handle);
482         ret = handle_close(handle);
483         status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
484         send_status(id, status);
485 }
486
487 void
488 process_read(void)
489 {
490         char buf[64*1024];
491         u_int32_t id, len;
492         int handle, fd, ret, status = SSH2_FX_FAILURE;
493         u_int64_t off;
494
495         id = get_int();
496         handle = get_handle();
497         off = get_int64();
498         len = get_int();
499
500         TRACE("read id %d handle %d off %lld len %d", id, handle, off, len);
501         if (len > sizeof buf) {
502                 len = sizeof buf;
503                 log("read change len %d", len);
504         }
505         fd = handle_to_fd(handle);
506         if (fd >= 0) {
507                 if (lseek(fd, off, SEEK_SET) < 0) {
508                         error("process_read: seek failed");
509                         status = errno_to_portable(errno);
510                 } else {
511                         ret = read(fd, buf, len);
512                         if (ret < 0) {
513                                 status = errno_to_portable(errno);
514                         } else if (ret == 0) {
515                                 status = SSH2_FX_EOF;
516                         } else {
517                                 send_data(id, buf, ret);
518                                 status = SSH2_FX_OK;
519                         }
520                 }
521         }
522         if (status != SSH2_FX_OK)
523                 send_status(id, status);
524 }
525
526 void
527 process_write(void)
528 {
529         u_int32_t id;
530         u_int64_t off;
531         u_int len;
532         int handle, fd, ret, status = SSH2_FX_FAILURE;
533         char *data;
534
535         id = get_int();
536         handle = get_handle();
537         off = get_int64();
538         data = get_string(&len);
539
540         TRACE("write id %d handle %d off %lld len %d", id, handle, off, len);
541         fd = handle_to_fd(handle);
542         if (fd >= 0) {
543                 if (lseek(fd, off, SEEK_SET) < 0) {
544                         status = errno_to_portable(errno);
545                         error("process_write: seek failed");
546                 } else {
547 /* XXX ATOMICIO ? */
548                         ret = write(fd, data, len);
549                         if (ret == -1) {
550                                 error("process_write: write failed");
551                                 status = errno_to_portable(errno);
552                         } else if (ret == len) {
553                                 status = SSH2_FX_OK;
554                         } else {
555                                 log("nothing at all written");
556                         }
557                 }
558         }
559         send_status(id, status);
560         xfree(data);
561 }
562
563 void
564 process_do_stat(int do_lstat)
565 {
566         Attrib a;
567         struct stat st;
568         u_int32_t id;
569         char *name;
570         int ret, status = SSH2_FX_FAILURE;
571
572         id = get_int();
573         name = get_string(NULL);
574         TRACE("%sstat id %d name %s", do_lstat ? "l" : "", id, name);
575         ret = do_lstat ? lstat(name, &st) : stat(name, &st);
576         if (ret < 0) {
577                 status = errno_to_portable(errno);
578         } else {
579                 stat_to_attrib(&st, &a);
580                 send_attrib(id, &a);
581                 status = SSH2_FX_OK;
582         }
583         if (status != SSH2_FX_OK)
584                 send_status(id, status);
585         xfree(name);
586 }
587
588 void
589 process_stat(void)
590 {
591         process_do_stat(0);
592 }
593
594 void
595 process_lstat(void)
596 {
597         process_do_stat(1);
598 }
599
600 void
601 process_fstat(void)
602 {
603         Attrib a;
604         struct stat st;
605         u_int32_t id;
606         int fd, ret, handle, status = SSH2_FX_FAILURE;
607
608         id = get_int();
609         handle = get_handle();
610         TRACE("fstat id %d handle %d", id, handle);
611         fd = handle_to_fd(handle);
612         if (fd  >= 0) {
613                 ret = fstat(fd, &st);
614                 if (ret < 0) {
615                         status = errno_to_portable(errno);
616                 } else {
617                         stat_to_attrib(&st, &a);
618                         send_attrib(id, &a);
619                         status = SSH2_FX_OK;
620                 }
621         }
622         if (status != SSH2_FX_OK)
623                 send_status(id, status);
624 }
625
626 struct timeval *
627 attrib_to_tv(Attrib *a)
628 {
629         static struct timeval tv[2];
630         tv[0].tv_sec = a->atime;
631         tv[0].tv_usec = 0;
632         tv[1].tv_sec = a->mtime;
633         tv[1].tv_usec = 0;
634         return tv;
635 }
636
637 void
638 process_setstat(void)
639 {
640         Attrib *a;
641         u_int32_t id;
642         char *name;
643         int ret;
644         int status = SSH2_FX_OK;
645
646         id = get_int();
647         name = get_string(NULL);
648         a = get_attrib();
649         TRACE("setstat id %d name %s", id, name);
650         if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
651                 ret = chmod(name, a->perm & 0777);
652                 if (ret == -1)
653                         status = errno_to_portable(errno);
654         }
655         if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
656                 ret = utimes(name, attrib_to_tv(a));
657                 if (ret == -1)
658                         status = errno_to_portable(errno);
659         }
660         send_status(id, status);
661         xfree(name);
662 }
663
664 void
665 process_fsetstat(void)
666 {
667         Attrib *a;
668         u_int32_t id;
669         int handle, fd, ret;
670         int status = SSH2_FX_OK;
671         char *name;
672
673         id = get_int();
674         handle = get_handle();
675         a = get_attrib();
676         TRACE("fsetstat id %d handle %d", id, handle);
677         fd = handle_to_fd(handle);
678         name = handle_to_name(handle);
679         if (fd < 0 || name == NULL) {
680                 status = SSH2_FX_FAILURE;
681         } else {
682                 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
683 #ifdef HAVE_FCHMOD
684                         ret = fchmod(fd, a->perm & 0777);
685 #else
686                         ret = chmod(name, a->perm & 0777);
687 #endif
688                         if (ret == -1)
689                                 status = errno_to_portable(errno);
690                 }
691                 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
692 #ifdef HAVE_FUTIMES
693                         ret = futimes(fd, attrib_to_tv(a));
694 #else
695                         ret = utimes(name, attrib_to_tv(a));
696 #endif
697                         if (ret == -1)
698                                 status = errno_to_portable(errno);
699                 }
700         }
701         send_status(id, status);
702 }
703
704 void
705 process_opendir(void)
706 {
707         DIR *dirp = NULL;
708         char *path;
709         int handle, status = SSH2_FX_FAILURE;
710         u_int32_t id;
711
712         id = get_int();
713         path = get_string(NULL);
714         TRACE("opendir id %d path %s", id, path);
715         dirp = opendir(path); 
716         if (dirp == NULL) {
717                 status = errno_to_portable(errno);
718         } else {
719                 handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp);
720                 if (handle < 0) {
721                         closedir(dirp);
722                 } else {
723                         send_handle(id, handle);
724                         status = SSH2_FX_OK;
725                 }
726                 
727         }
728         if (status != SSH2_FX_OK)
729                 send_status(id, status);
730         xfree(path);
731 }
732
733 /*
734  * drwxr-xr-x    5 markus   markus       1024 Jan 13 18:39 .ssh
735  */
736 char *
737 ls_file(char *name, struct stat *st)
738 {
739         int sz = 0;
740         struct passwd *pw;
741         struct group *gr;
742         struct tm *ltime = localtime(&st->st_mtime);
743         char *user, *group;
744         char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1];
745
746         strmode(st->st_mode, mode);
747         if ((pw = getpwuid(st->st_uid)) != NULL) {
748                 user = pw->pw_name;
749         } else {
750                 snprintf(ubuf, sizeof ubuf, "%d", st->st_uid);
751                 user = ubuf;
752         }
753         if ((gr = getgrgid(st->st_gid)) != NULL) {
754                 group = gr->gr_name;
755         } else {
756                 snprintf(gbuf, sizeof gbuf, "%d", st->st_gid);
757                 group = gbuf;
758         }
759         if (ltime != NULL) {
760                 if (time(NULL) - st->st_mtime < (365*24*60*60)/2)
761                         sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime);
762                 else
763                         sz = strftime(tbuf, sizeof tbuf, "%b %e  %Y", ltime);
764         }
765         if (sz == 0)
766                 tbuf[0] = '\0';
767         snprintf(buf, sizeof buf, "%s %3d %-8.8s %-8.8s %8qd %s %s", mode,
768             st->st_nlink, user, group, (long long)st->st_size, tbuf, name);
769         return xstrdup(buf);
770 }
771
772 void
773 process_readdir(void)
774 {
775         DIR *dirp;
776         struct dirent *dp;
777         char *path;
778         int handle;
779         u_int32_t id;
780
781         id = get_int();
782         handle = get_handle();
783         TRACE("readdir id %d handle %d", id, handle);
784         dirp = handle_to_dir(handle);
785         path = handle_to_name(handle);
786         if (dirp == NULL || path == NULL) {
787                 send_status(id, SSH2_FX_FAILURE);
788         } else {
789                 struct stat st;
790                 char pathname[1024];
791                 Stat *stats;
792                 int nstats = 10, count = 0, i;
793                 stats = xmalloc(nstats * sizeof(Stat));
794                 while ((dp = readdir(dirp)) != NULL) {
795                         if (count >= nstats) {
796                                 nstats *= 2;
797                                 stats = xrealloc(stats, nstats * sizeof(Stat));
798                         }
799 /* XXX OVERFLOW ? */
800                         snprintf(pathname, sizeof pathname,
801                             "%s/%s", path, dp->d_name);
802                         if (lstat(pathname, &st) < 0)
803                                 continue;
804                         stat_to_attrib(&st, &(stats[count].attrib));
805                         stats[count].name = xstrdup(dp->d_name);
806                         stats[count].long_name = ls_file(dp->d_name, &st);
807                         count++;
808                         /* send up to 100 entries in one message */
809                         /* XXX check packet size instead */
810                         if (count == 100)
811                                 break;
812                 }
813                 if (count > 0) {
814                         send_names(id, count, stats);
815                         for(i = 0; i < count; i++) {
816                                 xfree(stats[i].name);
817                                 xfree(stats[i].long_name);
818                         }
819                 } else {
820                         send_status(id, SSH2_FX_EOF);
821                 }
822                 xfree(stats);
823         }
824 }
825
826 void
827 process_remove(void)
828 {
829         char *name;
830         u_int32_t id;
831         int status = SSH2_FX_FAILURE;
832         int ret;
833
834         id = get_int();
835         name = get_string(NULL);
836         TRACE("remove id %d name %s", id, name);
837         ret = unlink(name);
838         status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
839         send_status(id, status);
840         xfree(name);
841 }
842
843 void
844 process_mkdir(void)
845 {
846         Attrib *a;
847         u_int32_t id;
848         char *name;
849         int ret, mode, status = SSH2_FX_FAILURE;
850
851         id = get_int();
852         name = get_string(NULL);
853         a = get_attrib();
854         mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
855             a->perm & 0777 : 0777;
856         TRACE("mkdir id %d name %s mode 0%o", id, name, mode);
857         ret = mkdir(name, mode);
858         status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
859         send_status(id, status);
860         xfree(name);
861 }
862
863 void
864 process_rmdir(void)
865 {
866         u_int32_t id;
867         char *name;
868         int ret, status;
869
870         id = get_int();
871         name = get_string(NULL);
872         TRACE("rmdir id %d name %s", id, name);
873         ret = rmdir(name);
874         status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
875         send_status(id, status);
876         xfree(name);
877 }
878
879 void
880 process_realpath(void)
881 {
882         char resolvedname[MAXPATHLEN];
883         u_int32_t id;
884         char *path;
885
886         id = get_int();
887         path = get_string(NULL);
888         if (path[0] == '\0') {
889                 xfree(path);
890                 path = xstrdup(".");
891         }
892         TRACE("realpath id %d path %s", id, path);
893         if (realpath(path, resolvedname) == NULL) {
894                 send_status(id, errno_to_portable(errno));
895         } else {
896                 Stat s;
897                 attrib_clear(&s.attrib);
898                 s.name = s.long_name = resolvedname;
899                 send_names(id, 1, &s);
900         }
901         xfree(path);
902 }
903
904 void
905 process_rename(void)
906 {
907         u_int32_t id;
908         struct stat st;
909         char *oldpath, *newpath;
910         int ret, status = SSH2_FX_FAILURE;
911
912         id = get_int();
913         oldpath = get_string(NULL);
914         newpath = get_string(NULL);
915         TRACE("rename id %d old %s new %s", id, oldpath, newpath);
916         /* fail if 'newpath' exists */
917         if (stat(newpath, &st) == -1) {
918                 ret = rename(oldpath, newpath);
919                 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
920         }
921         send_status(id, status);
922         xfree(oldpath);
923         xfree(newpath);
924 }
925
926 void
927 process_extended(void)
928 {
929         u_int32_t id;
930         char *request;
931
932         id = get_int();
933         request = get_string(NULL);
934         send_status(id, SSH2_FX_OP_UNSUPPORTED);                /* MUST */
935         xfree(request);
936 }
937
938 /* stolen from ssh-agent */
939
940 void
941 process(void)
942 {
943         u_int msg_len;
944         u_int type;
945         u_char *cp;
946
947         if (buffer_len(&iqueue) < 5)
948                 return;         /* Incomplete message. */
949         cp = (u_char *) buffer_ptr(&iqueue);
950         msg_len = GET_32BIT(cp);
951         if (msg_len > 256 * 1024) {
952                 error("bad message ");
953                 exit(11);
954         }
955         if (buffer_len(&iqueue) < msg_len + 4)
956                 return;
957         buffer_consume(&iqueue, 4);
958         type = buffer_get_char(&iqueue);
959         switch (type) {
960         case SSH2_FXP_INIT:
961                 process_init();
962                 break;
963         case SSH2_FXP_OPEN:
964                 process_open();
965                 break;
966         case SSH2_FXP_CLOSE:
967                 process_close();
968                 break;
969         case SSH2_FXP_READ:
970                 process_read();
971                 break;
972         case SSH2_FXP_WRITE:
973                 process_write();
974                 break;
975         case SSH2_FXP_LSTAT:
976                 process_lstat();
977                 break;
978         case SSH2_FXP_FSTAT:
979                 process_fstat();
980                 break;
981         case SSH2_FXP_SETSTAT:
982                 process_setstat();
983                 break;
984         case SSH2_FXP_FSETSTAT:
985                 process_fsetstat();
986                 break;
987         case SSH2_FXP_OPENDIR:
988                 process_opendir();
989                 break;
990         case SSH2_FXP_READDIR:
991                 process_readdir();
992                 break;
993         case SSH2_FXP_REMOVE:
994                 process_remove();
995                 break;
996         case SSH2_FXP_MKDIR:
997                 process_mkdir();
998                 break;
999         case SSH2_FXP_RMDIR:
1000                 process_rmdir();
1001                 break;
1002         case SSH2_FXP_REALPATH:
1003                 process_realpath();
1004                 break;
1005         case SSH2_FXP_STAT:
1006                 process_stat();
1007                 break;
1008         case SSH2_FXP_RENAME:
1009                 process_rename();
1010                 break;
1011         case SSH2_FXP_EXTENDED:
1012                 process_extended();
1013                 break;
1014         default:
1015                 error("Unknown message %d", type);
1016                 break;
1017         }
1018 }
1019
1020 int
1021 main(int ac, char **av)
1022 {
1023         fd_set rset, wset;
1024         int in, out, max;
1025         ssize_t len, olen;
1026
1027         __progname = get_progname(av[0]);
1028         handle_init();
1029
1030 #ifdef DEBUG_SFTP_SERVER
1031         log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
1032 #endif
1033
1034         in = dup(STDIN_FILENO);
1035         out = dup(STDOUT_FILENO);
1036
1037         max = 0;
1038         if (in > max)
1039                 max = in;
1040         if (out > max)
1041                 max = out;
1042
1043         buffer_init(&iqueue);
1044         buffer_init(&oqueue);
1045
1046         for (;;) {
1047                 FD_ZERO(&rset);
1048                 FD_ZERO(&wset);
1049
1050                 FD_SET(in, &rset);
1051                 olen = buffer_len(&oqueue);
1052                 if (olen > 0)
1053                         FD_SET(out, &wset);
1054
1055                 if (select(max+1, &rset, &wset, NULL, NULL) < 0) {
1056                         if (errno == EINTR)
1057                                 continue;
1058                         exit(2);
1059                 }
1060
1061                 /* copy stdin to iqueue */
1062                 if (FD_ISSET(in, &rset)) {
1063                         char buf[4*4096];
1064                         len = read(in, buf, sizeof buf);
1065                         if (len == 0) {
1066                                 debug("read eof");
1067                                 exit(0);
1068                         } else if (len < 0) {
1069                                 error("read error");
1070                                 exit(1);
1071                         } else {
1072                                 buffer_append(&iqueue, buf, len);
1073                         }
1074                 }
1075                 /* send oqueue to stdout */
1076                 if (FD_ISSET(out, &wset)) {
1077                         len = write(out, buffer_ptr(&oqueue), olen);
1078                         if (len < 0) {
1079                                 error("write error");
1080                                 exit(1);
1081                         } else {
1082                                 buffer_consume(&oqueue, len);
1083                         }
1084                 }
1085                 /* process requests from client */
1086                 process();
1087         }
1088 }
This page took 0.454941 seconds and 5 git commands to generate.