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