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