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