]> andersk Git - gssapi-openssh.git/blame - openssh/sftp-server.c
Re-import of OpenSSH 3.7.1p2 (Chase\!)
[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"
9cb1827b 25RCSID("$OpenBSD: sftp-server.c,v 1.43 2003/06/25 22:39:36 miod 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
0fff78ff 55/* portable attributes, etc. */
3c0ef626 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
9cb1827b 152handle_new(int use, char *name, int fd, DIR *dirp)
3c0ef626 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;
6a9b3198 161 handles[i].name = xstrdup(name);
3c0ef626 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
9cb1827b 187handle_from_string(char *handle, u_int hlen)
3c0ef626 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;
6a9b3198 233 xfree(handles[handle].name);
3c0ef626 234 } else if (handle_is_ok(handle, HANDLE_DIR)) {
235 ret = closedir(handles[handle].dirp);
236 handles[handle].use = HANDLE_UNUSED;
6a9b3198 237 xfree(handles[handle].name);
3c0ef626 238 } else {
239 errno = ENOENT;
240 }
241 return ret;
242}
243
244static int
245get_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
260static void
261send_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
270static void
271send_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
680cee3b 287 TRACE("sent status id %u error %u", id, error);
3c0ef626 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}
300static void
9cb1827b 301send_data_or_handle(char type, u_int32_t id, char *data, int dlen)
3c0ef626 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
313static void
9cb1827b 314send_data(u_int32_t id, char *data, int dlen)
3c0ef626 315{
680cee3b 316 TRACE("sent data id %u len %d", id, dlen);
3c0ef626 317 send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
318}
319
320static void
321send_handle(u_int32_t id, int handle)
322{
323 char *string;
324 int hlen;
325
326 handle_to_string(handle, &string, &hlen);
680cee3b 327 TRACE("sent handle id %u handle %d", id, handle);
3c0ef626 328 send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
329 xfree(string);
330}
331
332static void
9cb1827b 333send_names(u_int32_t id, int count, Stat *stats)
3c0ef626 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);
680cee3b 342 TRACE("sent names id %u count %d", id, count);
3c0ef626 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
352static void
9cb1827b 353send_attrib(u_int32_t id, Attrib *a)
3c0ef626 354{
355 Buffer msg;
356
680cee3b 357 TRACE("sent attrib id %u have 0x%x", id, a->flags);
3c0ef626 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
368static void
369process_init(void)
370{
371 Buffer msg;
372
f5799ae1 373 version = get_int();
3c0ef626 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
382static void
383process_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;
680cee3b 396 TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode);
3c0ef626 397 fd = open(name, flags, mode);
398 if (fd < 0) {
399 status = errno_to_portable(errno);
400 } else {
6a9b3198 401 handle = handle_new(HANDLE_FILE, name, fd, NULL);
3c0ef626 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
414static void
415process_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();
680cee3b 422 TRACE("close id %u handle %d", id, handle);
3c0ef626 423 ret = handle_close(handle);
424 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
425 send_status(id, status);
426}
427
428static void
429process_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
680cee3b 441 TRACE("read id %u handle %d off %llu len %d", id, handle,
3c0ef626 442 (u_int64_t)off, len);
443 if (len > sizeof buf) {
444 len = sizeof buf;
0fff78ff 445 logit("read change len %d", len);
3c0ef626 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
468static void
469process_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
680cee3b 482 TRACE("write id %u handle %d off %llu len %d", id, handle,
3c0ef626 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 {
0fff78ff 498 logit("nothing at all written");
3c0ef626 499 }
500 }
501 }
502 send_status(id, status);
503 xfree(data);
504}
505
506static void
507process_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);
680cee3b 517 TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name);
3c0ef626 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
531static void
532process_stat(void)
533{
534 process_do_stat(0);
535}
536
537static void
538process_lstat(void)
539{
540 process_do_stat(1);
541}
542
543static void
544process_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();
680cee3b 553 TRACE("fstat id %u handle %d", id, handle);
3c0ef626 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
569static struct timeval *
9cb1827b 570attrib_to_tv(Attrib *a)
3c0ef626 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
581static void
582process_setstat(void)
583{
584 Attrib *a;
585 u_int32_t id;
586 char *name;
680cee3b 587 int status = SSH2_FX_OK, ret;
3c0ef626 588
589 id = get_int();
590 name = get_string(NULL);
591 a = get_attrib();
680cee3b 592 TRACE("setstat id %u name %s", id, name);
e9a17296 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 }
3c0ef626 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
617static void
618process_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();
680cee3b 629 TRACE("fsetstat id %u handle %d", id, handle);
3c0ef626 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 {
e9a17296 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 }
3c0ef626 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
671static void
672process_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);
680cee3b 681 TRACE("opendir id %u path %s", id, path);
3c0ef626 682 dirp = opendir(path);
683 if (dirp == NULL) {
684 status = errno_to_portable(errno);
685 } else {
6a9b3198 686 handle = handle_new(HANDLE_DIR, path, 0, dirp);
3c0ef626 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
3c0ef626 700static void
701process_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();
680cee3b 711 TRACE("readdir id %u handle %d", id, handle);
3c0ef626 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;
680cee3b 721
3c0ef626 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);
41b2f314 735 stats[count].long_name = ls_file(dp->d_name, &st, 0);
3c0ef626 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);
e9a17296 744 for (i = 0; i < count; i++) {
3c0ef626 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
755static void
756process_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);
680cee3b 765 TRACE("remove id %u name %s", id, name);
3c0ef626 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
772static void
773process_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;
680cee3b 785 TRACE("mkdir id %u name %s mode 0%o", id, name, mode);
3c0ef626 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
792static void
793process_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);
680cee3b 801 TRACE("rmdir id %u name %s", id, name);
3c0ef626 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
808static void
809process_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 }
680cee3b 821 TRACE("realpath id %u path %s", id, path);
3c0ef626 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
833static void
834process_rename(void)
835{
836 u_int32_t id;
3c0ef626 837 char *oldpath, *newpath;
6a9b3198 838 int status;
839 struct stat sb;
3c0ef626 840
841 id = get_int();
842 oldpath = get_string(NULL);
843 newpath = get_string(NULL);
680cee3b 844 TRACE("rename id %u old %s new %s", id, oldpath, newpath);
6a9b3198 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;
3c0ef626 863 }
864 send_status(id, status);
865 xfree(oldpath);
866 xfree(newpath);
867}
868
869static void
870process_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);
680cee3b 879 TRACE("readlink id %u path %s", id, path);
3c0ef626 880 if ((len = readlink(path, link, sizeof(link) - 1)) == -1)
881 send_status(id, errno_to_portable(errno));
882 else {
883 Stat s;
e9a17296 884
3c0ef626 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
893static void
894process_symlink(void)
895{
896 u_int32_t id;
3c0ef626 897 char *oldpath, *newpath;
6a9b3198 898 int ret, status;
3c0ef626 899
900 id = get_int();
901 oldpath = get_string(NULL);
902 newpath = get_string(NULL);
680cee3b 903 TRACE("symlink id %u old %s new %s", id, oldpath, newpath);
6a9b3198 904 /* this will fail if 'newpath' exists */
905 ret = symlink(oldpath, newpath);
906 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
3c0ef626 907 send_status(id, status);
908 xfree(oldpath);
909 xfree(newpath);
910}
911
912static void
913process_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
926static void
927process(void)
928{
929 u_int msg_len;
f5799ae1 930 u_int buf_len;
931 u_int consumed;
3c0ef626 932 u_int type;
933 u_char *cp;
934
f5799ae1 935 buf_len = buffer_len(&iqueue);
936 if (buf_len < 5)
3c0ef626 937 return; /* Incomplete message. */
e9a17296 938 cp = buffer_ptr(&iqueue);
3c0ef626 939 msg_len = GET_32BIT(cp);
940 if (msg_len > 256 * 1024) {
941 error("bad message ");
942 exit(11);
943 }
f5799ae1 944 if (buf_len < msg_len + 4)
3c0ef626 945 return;
946 buffer_consume(&iqueue, 4);
f5799ae1 947 buf_len -= 4;
3c0ef626 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 }
f5799ae1 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);
3c0ef626 1022}
1023
1024int
1025main(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
0fff78ff 1033 __progname = ssh_get_progname(av[0]);
3c0ef626 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.685325 seconds and 5 git commands to generate.