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