]> andersk Git - gssapi-openssh.git/blame - openssh/sftp-server.c
Import of OpenSSH 3.4p1
[gssapi-openssh.git] / openssh / sftp-server.c
CommitLineData
3c0ef626 1/*
e9a17296 2 * Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved.
3c0ef626 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"
680cee3b 25RCSID("$OpenBSD: sftp-server.c,v 1.37 2002/06/24 17:57:20 deraadt Exp $");
3c0ef626 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
e9a17296 147 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
3c0ef626 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
e9a17296 156 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
3c0ef626 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
680cee3b 285 TRACE("sent status id %u error %u", id, error);
3c0ef626 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{
680cee3b 314 TRACE("sent data id %u len %d", id, dlen);
3c0ef626 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);
680cee3b 325 TRACE("sent handle id %u handle %d", id, handle);
3c0ef626 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);
680cee3b 340 TRACE("sent names id %u count %d", id, count);
3c0ef626 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
680cee3b 355 TRACE("sent attrib id %u have 0x%x", id, a->flags);
3c0ef626 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
f5799ae1 371 version = get_int();
3c0ef626 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;
680cee3b 394 TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode);
3c0ef626 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();
680cee3b 420 TRACE("close id %u handle %d", id, handle);
3c0ef626 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
680cee3b 439 TRACE("read id %u handle %d off %llu len %d", id, handle,
3c0ef626 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
680cee3b 480 TRACE("write id %u handle %d off %llu len %d", id, handle,
3c0ef626 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);
680cee3b 515 TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name);
3c0ef626 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();
680cee3b 551 TRACE("fstat id %u handle %d", id, handle);
3c0ef626 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;
680cee3b 585 int status = SSH2_FX_OK, ret;
3c0ef626 586
587 id = get_int();
588 name = get_string(NULL);
589 a = get_attrib();
680cee3b 590 TRACE("setstat id %u name %s", id, name);
e9a17296 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 }
3c0ef626 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();
680cee3b 627 TRACE("fsetstat id %u handle %d", id, handle);
3c0ef626 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 {
e9a17296 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 }
3c0ef626 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);
680cee3b 679 TRACE("opendir id %u path %s", id, path);
3c0ef626 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
698/*
699 * drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh
700 */
701static char *
702ls_file(char *name, struct stat *st)
703{
704 int ulen, glen, sz = 0;
705 struct passwd *pw;
706 struct group *gr;
707 struct tm *ltime = localtime(&st->st_mtime);
708 char *user, *group;
709 char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1];
710
711 strmode(st->st_mode, mode);
712 if ((pw = getpwuid(st->st_uid)) != NULL) {
713 user = pw->pw_name;
714 } else {
680cee3b 715 snprintf(ubuf, sizeof ubuf, "%u", (u_int)st->st_uid);
3c0ef626 716 user = ubuf;
717 }
718 if ((gr = getgrgid(st->st_gid)) != NULL) {
719 group = gr->gr_name;
720 } else {
680cee3b 721 snprintf(gbuf, sizeof gbuf, "%u", (u_int)st->st_gid);
3c0ef626 722 group = gbuf;
723 }
724 if (ltime != NULL) {
725 if (time(NULL) - st->st_mtime < (365*24*60*60)/2)
726 sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime);
727 else
728 sz = strftime(tbuf, sizeof tbuf, "%b %e %Y", ltime);
729 }
730 if (sz == 0)
731 tbuf[0] = '\0';
732 ulen = MAX(strlen(user), 8);
733 glen = MAX(strlen(group), 8);
734 snprintf(buf, sizeof buf, "%s %3d %-*s %-*s %8llu %s %s", mode,
735 st->st_nlink, ulen, user, glen, group,
736 (u_int64_t)st->st_size, tbuf, name);
737 return xstrdup(buf);
738}
739
740static void
741process_readdir(void)
742{
743 DIR *dirp;
744 struct dirent *dp;
745 char *path;
746 int handle;
747 u_int32_t id;
748
749 id = get_int();
750 handle = get_handle();
680cee3b 751 TRACE("readdir id %u handle %d", id, handle);
3c0ef626 752 dirp = handle_to_dir(handle);
753 path = handle_to_name(handle);
754 if (dirp == NULL || path == NULL) {
755 send_status(id, SSH2_FX_FAILURE);
756 } else {
757 struct stat st;
758 char pathname[1024];
759 Stat *stats;
760 int nstats = 10, count = 0, i;
680cee3b 761
3c0ef626 762 stats = xmalloc(nstats * sizeof(Stat));
763 while ((dp = readdir(dirp)) != NULL) {
764 if (count >= nstats) {
765 nstats *= 2;
766 stats = xrealloc(stats, nstats * sizeof(Stat));
767 }
768/* XXX OVERFLOW ? */
769 snprintf(pathname, sizeof pathname, "%s%s%s", path,
770 strcmp(path, "/") ? "/" : "", dp->d_name);
771 if (lstat(pathname, &st) < 0)
772 continue;
773 stat_to_attrib(&st, &(stats[count].attrib));
774 stats[count].name = xstrdup(dp->d_name);
775 stats[count].long_name = ls_file(dp->d_name, &st);
776 count++;
777 /* send up to 100 entries in one message */
778 /* XXX check packet size instead */
779 if (count == 100)
780 break;
781 }
782 if (count > 0) {
783 send_names(id, count, stats);
e9a17296 784 for (i = 0; i < count; i++) {
3c0ef626 785 xfree(stats[i].name);
786 xfree(stats[i].long_name);
787 }
788 } else {
789 send_status(id, SSH2_FX_EOF);
790 }
791 xfree(stats);
792 }
793}
794
795static void
796process_remove(void)
797{
798 char *name;
799 u_int32_t id;
800 int status = SSH2_FX_FAILURE;
801 int ret;
802
803 id = get_int();
804 name = get_string(NULL);
680cee3b 805 TRACE("remove id %u name %s", id, name);
3c0ef626 806 ret = unlink(name);
807 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
808 send_status(id, status);
809 xfree(name);
810}
811
812static void
813process_mkdir(void)
814{
815 Attrib *a;
816 u_int32_t id;
817 char *name;
818 int ret, mode, status = SSH2_FX_FAILURE;
819
820 id = get_int();
821 name = get_string(NULL);
822 a = get_attrib();
823 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
824 a->perm & 0777 : 0777;
680cee3b 825 TRACE("mkdir id %u name %s mode 0%o", id, name, mode);
3c0ef626 826 ret = mkdir(name, mode);
827 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
828 send_status(id, status);
829 xfree(name);
830}
831
832static void
833process_rmdir(void)
834{
835 u_int32_t id;
836 char *name;
837 int ret, status;
838
839 id = get_int();
840 name = get_string(NULL);
680cee3b 841 TRACE("rmdir id %u name %s", id, name);
3c0ef626 842 ret = rmdir(name);
843 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
844 send_status(id, status);
845 xfree(name);
846}
847
848static void
849process_realpath(void)
850{
851 char resolvedname[MAXPATHLEN];
852 u_int32_t id;
853 char *path;
854
855 id = get_int();
856 path = get_string(NULL);
857 if (path[0] == '\0') {
858 xfree(path);
859 path = xstrdup(".");
860 }
680cee3b 861 TRACE("realpath id %u path %s", id, path);
3c0ef626 862 if (realpath(path, resolvedname) == NULL) {
863 send_status(id, errno_to_portable(errno));
864 } else {
865 Stat s;
866 attrib_clear(&s.attrib);
867 s.name = s.long_name = resolvedname;
868 send_names(id, 1, &s);
869 }
870 xfree(path);
871}
872
873static void
874process_rename(void)
875{
876 u_int32_t id;
877 struct stat st;
878 char *oldpath, *newpath;
879 int ret, status = SSH2_FX_FAILURE;
880
881 id = get_int();
882 oldpath = get_string(NULL);
883 newpath = get_string(NULL);
680cee3b 884 TRACE("rename id %u old %s new %s", id, oldpath, newpath);
3c0ef626 885 /* fail if 'newpath' exists */
886 if (stat(newpath, &st) == -1) {
887 ret = rename(oldpath, newpath);
888 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
889 }
890 send_status(id, status);
891 xfree(oldpath);
892 xfree(newpath);
893}
894
895static void
896process_readlink(void)
897{
898 u_int32_t id;
899 int len;
900 char link[MAXPATHLEN];
901 char *path;
902
903 id = get_int();
904 path = get_string(NULL);
680cee3b 905 TRACE("readlink id %u path %s", id, path);
3c0ef626 906 if ((len = readlink(path, link, sizeof(link) - 1)) == -1)
907 send_status(id, errno_to_portable(errno));
908 else {
909 Stat s;
e9a17296 910
3c0ef626 911 link[len] = '\0';
912 attrib_clear(&s.attrib);
913 s.name = s.long_name = link;
914 send_names(id, 1, &s);
915 }
916 xfree(path);
917}
918
919static void
920process_symlink(void)
921{
922 u_int32_t id;
923 struct stat st;
924 char *oldpath, *newpath;
925 int ret, status = SSH2_FX_FAILURE;
926
927 id = get_int();
928 oldpath = get_string(NULL);
929 newpath = get_string(NULL);
680cee3b 930 TRACE("symlink id %u old %s new %s", id, oldpath, newpath);
3c0ef626 931 /* fail if 'newpath' exists */
932 if (stat(newpath, &st) == -1) {
933 ret = symlink(oldpath, newpath);
934 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
935 }
936 send_status(id, status);
937 xfree(oldpath);
938 xfree(newpath);
939}
940
941static void
942process_extended(void)
943{
944 u_int32_t id;
945 char *request;
946
947 id = get_int();
948 request = get_string(NULL);
949 send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
950 xfree(request);
951}
952
953/* stolen from ssh-agent */
954
955static void
956process(void)
957{
958 u_int msg_len;
f5799ae1 959 u_int buf_len;
960 u_int consumed;
3c0ef626 961 u_int type;
962 u_char *cp;
963
f5799ae1 964 buf_len = buffer_len(&iqueue);
965 if (buf_len < 5)
3c0ef626 966 return; /* Incomplete message. */
e9a17296 967 cp = buffer_ptr(&iqueue);
3c0ef626 968 msg_len = GET_32BIT(cp);
969 if (msg_len > 256 * 1024) {
970 error("bad message ");
971 exit(11);
972 }
f5799ae1 973 if (buf_len < msg_len + 4)
3c0ef626 974 return;
975 buffer_consume(&iqueue, 4);
f5799ae1 976 buf_len -= 4;
3c0ef626 977 type = buffer_get_char(&iqueue);
978 switch (type) {
979 case SSH2_FXP_INIT:
980 process_init();
981 break;
982 case SSH2_FXP_OPEN:
983 process_open();
984 break;
985 case SSH2_FXP_CLOSE:
986 process_close();
987 break;
988 case SSH2_FXP_READ:
989 process_read();
990 break;
991 case SSH2_FXP_WRITE:
992 process_write();
993 break;
994 case SSH2_FXP_LSTAT:
995 process_lstat();
996 break;
997 case SSH2_FXP_FSTAT:
998 process_fstat();
999 break;
1000 case SSH2_FXP_SETSTAT:
1001 process_setstat();
1002 break;
1003 case SSH2_FXP_FSETSTAT:
1004 process_fsetstat();
1005 break;
1006 case SSH2_FXP_OPENDIR:
1007 process_opendir();
1008 break;
1009 case SSH2_FXP_READDIR:
1010 process_readdir();
1011 break;
1012 case SSH2_FXP_REMOVE:
1013 process_remove();
1014 break;
1015 case SSH2_FXP_MKDIR:
1016 process_mkdir();
1017 break;
1018 case SSH2_FXP_RMDIR:
1019 process_rmdir();
1020 break;
1021 case SSH2_FXP_REALPATH:
1022 process_realpath();
1023 break;
1024 case SSH2_FXP_STAT:
1025 process_stat();
1026 break;
1027 case SSH2_FXP_RENAME:
1028 process_rename();
1029 break;
1030 case SSH2_FXP_READLINK:
1031 process_readlink();
1032 break;
1033 case SSH2_FXP_SYMLINK:
1034 process_symlink();
1035 break;
1036 case SSH2_FXP_EXTENDED:
1037 process_extended();
1038 break;
1039 default:
1040 error("Unknown message %d", type);
1041 break;
1042 }
f5799ae1 1043 /* discard the remaining bytes from the current packet */
1044 if (buf_len < buffer_len(&iqueue))
1045 fatal("iqueue grows");
1046 consumed = buf_len - buffer_len(&iqueue);
1047 if (msg_len < consumed)
1048 fatal("msg_len %d < consumed %d", msg_len, consumed);
1049 if (msg_len > consumed)
1050 buffer_consume(&iqueue, msg_len - consumed);
3c0ef626 1051}
1052
1053int
1054main(int ac, char **av)
1055{
1056 fd_set *rset, *wset;
1057 int in, out, max;
1058 ssize_t len, olen, set_size;
1059
1060 /* XXX should use getopt */
1061
1062 __progname = get_progname(av[0]);
1063 handle_init();
1064
1065#ifdef DEBUG_SFTP_SERVER
1066 log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
1067#endif
1068
1069 in = dup(STDIN_FILENO);
1070 out = dup(STDOUT_FILENO);
1071
1072#ifdef HAVE_CYGWIN
1073 setmode(in, O_BINARY);
1074 setmode(out, O_BINARY);
1075#endif
1076
1077 max = 0;
1078 if (in > max)
1079 max = in;
1080 if (out > max)
1081 max = out;
1082
1083 buffer_init(&iqueue);
1084 buffer_init(&oqueue);
1085
1086 set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
1087 rset = (fd_set *)xmalloc(set_size);
1088 wset = (fd_set *)xmalloc(set_size);
1089
1090 for (;;) {
1091 memset(rset, 0, set_size);
1092 memset(wset, 0, set_size);
1093
1094 FD_SET(in, rset);
1095 olen = buffer_len(&oqueue);
1096 if (olen > 0)
1097 FD_SET(out, wset);
1098
1099 if (select(max+1, rset, wset, NULL, NULL) < 0) {
1100 if (errno == EINTR)
1101 continue;
1102 exit(2);
1103 }
1104
1105 /* copy stdin to iqueue */
1106 if (FD_ISSET(in, rset)) {
1107 char buf[4*4096];
1108 len = read(in, buf, sizeof buf);
1109 if (len == 0) {
1110 debug("read eof");
1111 exit(0);
1112 } else if (len < 0) {
1113 error("read error");
1114 exit(1);
1115 } else {
1116 buffer_append(&iqueue, buf, len);
1117 }
1118 }
1119 /* send oqueue to stdout */
1120 if (FD_ISSET(out, wset)) {
1121 len = write(out, buffer_ptr(&oqueue), olen);
1122 if (len < 0) {
1123 error("write error");
1124 exit(1);
1125 } else {
1126 buffer_consume(&oqueue, len);
1127 }
1128 }
1129 /* process requests from client */
1130 process();
1131 }
1132}
This page took 0.330443 seconds and 5 git commands to generate.