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