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