source: trunk/third/openssh/sftp-server.c @ 18759

Revision 18759, 21.7 KB checked in by zacheiss, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18758, which included commits to RCS files with non-trunk default branches.
Line 
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"
25RCSID("$OpenBSD: sftp-server.c,v 1.38 2002/09/11 22:41:50 djm 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
43extern char *__progname;
44#else
45char *__progname;
46#endif
47
48/* input and output queue */
49Buffer iqueue;
50Buffer oqueue;
51
52/* Version of client */
53int version;
54
55/* portable attibutes, etc. */
56
57typedef struct Stat Stat;
58
59struct Stat {
60        char *name;
61        char *long_name;
62        Attrib attrib;
63};
64
65static int
66errno_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
96static int
97flags_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
118static Attrib *
119get_attrib(void)
120{
121        return decode_attrib(&iqueue);
122}
123
124/* handle handles */
125
126typedef struct Handle Handle;
127struct Handle {
128        int use;
129        DIR *dirp;
130        int fd;
131        char *name;
132};
133
134enum {
135        HANDLE_UNUSED,
136        HANDLE_DIR,
137        HANDLE_FILE
138};
139
140Handle  handles[100];
141
142static void
143handle_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
151static int
152handle_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
168static int
169handle_is_ok(int i, int type)
170{
171        return i >= 0 && i < sizeof(handles)/sizeof(Handle) &&
172            handles[i].use == type;
173}
174
175static int
176handle_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
186static int
187handle_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
200static char *
201handle_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
209static DIR *
210handle_to_dir(int handle)
211{
212        if (handle_is_ok(handle, HANDLE_DIR))
213                return handles[handle].dirp;
214        return NULL;
215}
216
217static int
218handle_to_fd(int handle)
219{
220        if (handle_is_ok(handle, HANDLE_FILE))
221                return handles[handle].fd;
222        return -1;
223}
224
225static int
226handle_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
242static int
243get_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
258static void
259send_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
268static void
269send_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 %u error %u", 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}
298static void
299send_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
311static void
312send_data(u_int32_t id, char *data, int dlen)
313{
314        TRACE("sent data id %u len %d", id, dlen);
315        send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
316}
317
318static void
319send_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 %u handle %d", id, handle);
326        send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
327        xfree(string);
328}
329
330static void
331send_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 %u 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
350static void
351send_attrib(u_int32_t id, Attrib *a)
352{
353        Buffer msg;
354
355        TRACE("sent attrib id %u 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
366static void
367process_init(void)
368{
369        Buffer msg;
370
371        version = get_int();
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
380static void
381process_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 %u 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
412static void
413process_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 %u 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
426static void
427process_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 %u 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
466static void
467process_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 %u 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
504static void
505process_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 %u 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
529static void
530process_stat(void)
531{
532        process_do_stat(0);
533}
534
535static void
536process_lstat(void)
537{
538        process_do_stat(1);
539}
540
541static void
542process_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 %u 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
567static struct timeval *
568attrib_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
579static void
580process_setstat(void)
581{
582        Attrib *a;
583        u_int32_t id;
584        char *name;
585        int status = SSH2_FX_OK, ret;
586
587        id = get_int();
588        name = get_string(NULL);
589        a = get_attrib();
590        TRACE("setstat id %u name %s", id, name);
591        if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
592                ret = truncate(name, a->size);
593                if (ret == -1)
594                        status = errno_to_portable(errno);
595        }
596        if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
597                ret = chmod(name, a->perm & 0777);
598                if (ret == -1)
599                        status = errno_to_portable(errno);
600        }
601        if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
602                ret = utimes(name, attrib_to_tv(a));
603                if (ret == -1)
604                        status = errno_to_portable(errno);
605        }
606        if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
607                ret = chown(name, a->uid, a->gid);
608                if (ret == -1)
609                        status = errno_to_portable(errno);
610        }
611        send_status(id, status);
612        xfree(name);
613}
614
615static void
616process_fsetstat(void)
617{
618        Attrib *a;
619        u_int32_t id;
620        int handle, fd, ret;
621        int status = SSH2_FX_OK;
622        char *name;
623
624        id = get_int();
625        handle = get_handle();
626        a = get_attrib();
627        TRACE("fsetstat id %u handle %d", id, handle);
628        fd = handle_to_fd(handle);
629        name = handle_to_name(handle);
630        if (fd < 0 || name == NULL) {
631                status = SSH2_FX_FAILURE;
632        } else {
633                if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
634                        ret = ftruncate(fd, a->size);
635                        if (ret == -1)
636                                status = errno_to_portable(errno);
637                }
638                if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
639#ifdef HAVE_FCHMOD
640                        ret = fchmod(fd, a->perm & 0777);
641#else
642                        ret = chmod(name, a->perm & 0777);
643#endif
644                        if (ret == -1)
645                                status = errno_to_portable(errno);
646                }
647                if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
648#ifdef HAVE_FUTIMES
649                        ret = futimes(fd, attrib_to_tv(a));
650#else
651                        ret = utimes(name, attrib_to_tv(a));
652#endif
653                        if (ret == -1)
654                                status = errno_to_portable(errno);
655                }
656                if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
657#ifdef HAVE_FCHOWN
658                        ret = fchown(fd, a->uid, a->gid);
659#else
660                        ret = chown(name, a->uid, a->gid);
661#endif
662                        if (ret == -1)
663                                status = errno_to_portable(errno);
664                }
665        }
666        send_status(id, status);
667}
668
669static void
670process_opendir(void)
671{
672        DIR *dirp = NULL;
673        char *path;
674        int handle, status = SSH2_FX_FAILURE;
675        u_int32_t id;
676
677        id = get_int();
678        path = get_string(NULL);
679        TRACE("opendir id %u path %s", id, path);
680        dirp = opendir(path);
681        if (dirp == NULL) {
682                status = errno_to_portable(errno);
683        } else {
684                handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp);
685                if (handle < 0) {
686                        closedir(dirp);
687                } else {
688                        send_handle(id, handle);
689                        status = SSH2_FX_OK;
690                }
691
692        }
693        if (status != SSH2_FX_OK)
694                send_status(id, status);
695        xfree(path);
696}
697
698static void
699process_readdir(void)
700{
701        DIR *dirp;
702        struct dirent *dp;
703        char *path;
704        int handle;
705        u_int32_t id;
706
707        id = get_int();
708        handle = get_handle();
709        TRACE("readdir id %u handle %d", id, handle);
710        dirp = handle_to_dir(handle);
711        path = handle_to_name(handle);
712        if (dirp == NULL || path == NULL) {
713                send_status(id, SSH2_FX_FAILURE);
714        } else {
715                struct stat st;
716                char pathname[1024];
717                Stat *stats;
718                int nstats = 10, count = 0, i;
719
720                stats = xmalloc(nstats * sizeof(Stat));
721                while ((dp = readdir(dirp)) != NULL) {
722                        if (count >= nstats) {
723                                nstats *= 2;
724                                stats = xrealloc(stats, nstats * sizeof(Stat));
725                        }
726/* XXX OVERFLOW ? */
727                        snprintf(pathname, sizeof pathname, "%s%s%s", path,
728                            strcmp(path, "/") ? "/" : "", dp->d_name);
729                        if (lstat(pathname, &st) < 0)
730                                continue;
731                        stat_to_attrib(&st, &(stats[count].attrib));
732                        stats[count].name = xstrdup(dp->d_name);
733                        stats[count].long_name = ls_file(dp->d_name, &st, 0);
734                        count++;
735                        /* send up to 100 entries in one message */
736                        /* XXX check packet size instead */
737                        if (count == 100)
738                                break;
739                }
740                if (count > 0) {
741                        send_names(id, count, stats);
742                        for (i = 0; i < count; i++) {
743                                xfree(stats[i].name);
744                                xfree(stats[i].long_name);
745                        }
746                } else {
747                        send_status(id, SSH2_FX_EOF);
748                }
749                xfree(stats);
750        }
751}
752
753static void
754process_remove(void)
755{
756        char *name;
757        u_int32_t id;
758        int status = SSH2_FX_FAILURE;
759        int ret;
760
761        id = get_int();
762        name = get_string(NULL);
763        TRACE("remove id %u name %s", id, name);
764        ret = unlink(name);
765        status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
766        send_status(id, status);
767        xfree(name);
768}
769
770static void
771process_mkdir(void)
772{
773        Attrib *a;
774        u_int32_t id;
775        char *name;
776        int ret, mode, status = SSH2_FX_FAILURE;
777
778        id = get_int();
779        name = get_string(NULL);
780        a = get_attrib();
781        mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
782            a->perm & 0777 : 0777;
783        TRACE("mkdir id %u name %s mode 0%o", id, name, mode);
784        ret = mkdir(name, mode);
785        status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
786        send_status(id, status);
787        xfree(name);
788}
789
790static void
791process_rmdir(void)
792{
793        u_int32_t id;
794        char *name;
795        int ret, status;
796
797        id = get_int();
798        name = get_string(NULL);
799        TRACE("rmdir id %u name %s", id, name);
800        ret = rmdir(name);
801        status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
802        send_status(id, status);
803        xfree(name);
804}
805
806static void
807process_realpath(void)
808{
809        char resolvedname[MAXPATHLEN];
810        u_int32_t id;
811        char *path;
812
813        id = get_int();
814        path = get_string(NULL);
815        if (path[0] == '\0') {
816                xfree(path);
817                path = xstrdup(".");
818        }
819        TRACE("realpath id %u path %s", id, path);
820        if (realpath(path, resolvedname) == NULL) {
821                send_status(id, errno_to_portable(errno));
822        } else {
823                Stat s;
824                attrib_clear(&s.attrib);
825                s.name = s.long_name = resolvedname;
826                send_names(id, 1, &s);
827        }
828        xfree(path);
829}
830
831static void
832process_rename(void)
833{
834        u_int32_t id;
835        struct stat st;
836        char *oldpath, *newpath;
837        int ret, status = SSH2_FX_FAILURE;
838
839        id = get_int();
840        oldpath = get_string(NULL);
841        newpath = get_string(NULL);
842        TRACE("rename id %u old %s new %s", id, oldpath, newpath);
843        /* fail if 'newpath' exists */
844        if (stat(newpath, &st) == -1) {
845                ret = rename(oldpath, newpath);
846                status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
847        }
848        send_status(id, status);
849        xfree(oldpath);
850        xfree(newpath);
851}
852
853static void
854process_readlink(void)
855{
856        u_int32_t id;
857        int len;
858        char link[MAXPATHLEN];
859        char *path;
860
861        id = get_int();
862        path = get_string(NULL);
863        TRACE("readlink id %u path %s", id, path);
864        if ((len = readlink(path, link, sizeof(link) - 1)) == -1)
865                send_status(id, errno_to_portable(errno));
866        else {
867                Stat s;
868
869                link[len] = '\0';
870                attrib_clear(&s.attrib);
871                s.name = s.long_name = link;
872                send_names(id, 1, &s);
873        }
874        xfree(path);
875}
876
877static void
878process_symlink(void)
879{
880        u_int32_t id;
881        struct stat st;
882        char *oldpath, *newpath;
883        int ret, status = SSH2_FX_FAILURE;
884
885        id = get_int();
886        oldpath = get_string(NULL);
887        newpath = get_string(NULL);
888        TRACE("symlink id %u old %s new %s", id, oldpath, newpath);
889        /* fail if 'newpath' exists */
890        if (stat(newpath, &st) == -1) {
891                ret = symlink(oldpath, newpath);
892                status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
893        }
894        send_status(id, status);
895        xfree(oldpath);
896        xfree(newpath);
897}
898
899static void
900process_extended(void)
901{
902        u_int32_t id;
903        char *request;
904
905        id = get_int();
906        request = get_string(NULL);
907        send_status(id, SSH2_FX_OP_UNSUPPORTED);                /* MUST */
908        xfree(request);
909}
910
911/* stolen from ssh-agent */
912
913static void
914process(void)
915{
916        u_int msg_len;
917        u_int buf_len;
918        u_int consumed;
919        u_int type;
920        u_char *cp;
921
922        buf_len = buffer_len(&iqueue);
923        if (buf_len < 5)
924                return;         /* Incomplete message. */
925        cp = buffer_ptr(&iqueue);
926        msg_len = GET_32BIT(cp);
927        if (msg_len > 256 * 1024) {
928                error("bad message ");
929                exit(11);
930        }
931        if (buf_len < msg_len + 4)
932                return;
933        buffer_consume(&iqueue, 4);
934        buf_len -= 4;
935        type = buffer_get_char(&iqueue);
936        switch (type) {
937        case SSH2_FXP_INIT:
938                process_init();
939                break;
940        case SSH2_FXP_OPEN:
941                process_open();
942                break;
943        case SSH2_FXP_CLOSE:
944                process_close();
945                break;
946        case SSH2_FXP_READ:
947                process_read();
948                break;
949        case SSH2_FXP_WRITE:
950                process_write();
951                break;
952        case SSH2_FXP_LSTAT:
953                process_lstat();
954                break;
955        case SSH2_FXP_FSTAT:
956                process_fstat();
957                break;
958        case SSH2_FXP_SETSTAT:
959                process_setstat();
960                break;
961        case SSH2_FXP_FSETSTAT:
962                process_fsetstat();
963                break;
964        case SSH2_FXP_OPENDIR:
965                process_opendir();
966                break;
967        case SSH2_FXP_READDIR:
968                process_readdir();
969                break;
970        case SSH2_FXP_REMOVE:
971                process_remove();
972                break;
973        case SSH2_FXP_MKDIR:
974                process_mkdir();
975                break;
976        case SSH2_FXP_RMDIR:
977                process_rmdir();
978                break;
979        case SSH2_FXP_REALPATH:
980                process_realpath();
981                break;
982        case SSH2_FXP_STAT:
983                process_stat();
984                break;
985        case SSH2_FXP_RENAME:
986                process_rename();
987                break;
988        case SSH2_FXP_READLINK:
989                process_readlink();
990                break;
991        case SSH2_FXP_SYMLINK:
992                process_symlink();
993                break;
994        case SSH2_FXP_EXTENDED:
995                process_extended();
996                break;
997        default:
998                error("Unknown message %d", type);
999                break;
1000        }
1001        /* discard the remaining bytes from the current packet */
1002        if (buf_len < buffer_len(&iqueue))
1003                fatal("iqueue grows");
1004        consumed = buf_len - buffer_len(&iqueue);
1005        if (msg_len < consumed)
1006                fatal("msg_len %d < consumed %d", msg_len, consumed);
1007        if (msg_len > consumed)
1008                buffer_consume(&iqueue, msg_len - consumed);
1009}
1010
1011int
1012main(int ac, char **av)
1013{
1014        fd_set *rset, *wset;
1015        int in, out, max;
1016        ssize_t len, olen, set_size;
1017
1018        /* XXX should use getopt */
1019
1020        __progname = get_progname(av[0]);
1021        handle_init();
1022
1023#ifdef DEBUG_SFTP_SERVER
1024        log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
1025#endif
1026
1027        in = dup(STDIN_FILENO);
1028        out = dup(STDOUT_FILENO);
1029
1030#ifdef HAVE_CYGWIN
1031        setmode(in, O_BINARY);
1032        setmode(out, O_BINARY);
1033#endif
1034
1035        max = 0;
1036        if (in > max)
1037                max = in;
1038        if (out > max)
1039                max = out;
1040
1041        buffer_init(&iqueue);
1042        buffer_init(&oqueue);
1043
1044        set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
1045        rset = (fd_set *)xmalloc(set_size);
1046        wset = (fd_set *)xmalloc(set_size);
1047
1048        for (;;) {
1049                memset(rset, 0, set_size);
1050                memset(wset, 0, set_size);
1051
1052                FD_SET(in, rset);
1053                olen = buffer_len(&oqueue);
1054                if (olen > 0)
1055                        FD_SET(out, wset);
1056
1057                if (select(max+1, rset, wset, NULL, NULL) < 0) {
1058                        if (errno == EINTR)
1059                                continue;
1060                        exit(2);
1061                }
1062
1063                /* copy stdin to iqueue */
1064                if (FD_ISSET(in, rset)) {
1065                        char buf[4*4096];
1066                        len = read(in, buf, sizeof buf);
1067                        if (len == 0) {
1068                                debug("read eof");
1069                                exit(0);
1070                        } else if (len < 0) {
1071                                error("read error");
1072                                exit(1);
1073                        } else {
1074                                buffer_append(&iqueue, buf, len);
1075                        }
1076                }
1077                /* send oqueue to stdout */
1078                if (FD_ISSET(out, wset)) {
1079                        len = write(out, buffer_ptr(&oqueue), olen);
1080                        if (len < 0) {
1081                                error("write error");
1082                                exit(1);
1083                        } else {
1084                                buffer_consume(&oqueue, len);
1085                        }
1086                }
1087                /* process requests from client */
1088                process();
1089        }
1090}
Note: See TracBrowser for help on using the repository browser.