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