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