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