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