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