]> andersk Git - openssh.git/blame - sftp-server.c
- dtucker@cvs.openbsd.org 2006/08/01 11:34:36
[openssh.git] / sftp-server.c
CommitLineData
ffa517a8 1/* $OpenBSD: sftp-server.c,v 1.68 2006/07/26 13:57:17 stevesk 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>
536c14e8 20#include <sys/param.h>
4095f623 21#include <sys/stat.h>
e264ac72 22#ifdef HAVE_SYS_TIME_H
23# include <sys/time.h>
24#endif
68e39d38 25
26#include <dirent.h>
028094f4 27#include <errno.h>
d3221cca 28#include <fcntl.h>
b1842393 29#include <pwd.h>
ffa517a8 30#include <stdlib.h>
00146caa 31#include <string.h>
b0f6943a 32#include <time.h>
00146caa 33#include <unistd.h>
b5e300c2 34
b5e300c2 35#include "buffer.h"
36#include "bufaux.h"
42f11eb2 37#include "log.h"
b5e300c2 38#include "xmalloc.h"
fd6168c1 39#include "misc.h"
a13880bb 40#include "uidswap.h"
b5e300c2 41
f546c780 42#include "sftp.h"
61e96248 43#include "sftp-common.h"
b5e300c2 44
45/* helper */
f546c780 46#define get_int64() buffer_get_int64(&iqueue);
b5e300c2 47#define get_int() buffer_get_int(&iqueue);
48#define get_string(lenp) buffer_get_string(&iqueue, lenp);
b5e300c2 49
a13880bb 50/* Our verbosity */
51LogLevel log_level = SYSLOG_LEVEL_ERROR;
52
53/* Our client */
54struct passwd *pw = NULL;
55char *client_addr = NULL;
260d427b 56
b5e300c2 57/* input and output queue */
58Buffer iqueue;
59Buffer oqueue;
60
3a7fe5ba 61/* Version of client */
62int version;
63
d7ded285 64/* portable attributes, etc. */
b5e300c2 65
b5e300c2 66typedef struct Stat Stat;
67
61e96248 68struct Stat {
b5e300c2 69 char *name;
70 char *long_name;
71 Attrib attrib;
72};
73
396c147e 74static int
b5e300c2 75errno_to_portable(int unixerrno)
76{
77 int ret = 0;
dce9bac5 78
b5e300c2 79 switch (unixerrno) {
80 case 0:
f546c780 81 ret = SSH2_FX_OK;
b5e300c2 82 break;
83 case ENOENT:
84 case ENOTDIR:
85 case EBADF:
86 case ELOOP:
f546c780 87 ret = SSH2_FX_NO_SUCH_FILE;
b5e300c2 88 break;
89 case EPERM:
90 case EACCES:
91 case EFAULT:
f546c780 92 ret = SSH2_FX_PERMISSION_DENIED;
b5e300c2 93 break;
94 case ENAMETOOLONG:
95 case EINVAL:
f546c780 96 ret = SSH2_FX_BAD_MESSAGE;
b5e300c2 97 break;
98 default:
f546c780 99 ret = SSH2_FX_FAILURE;
b5e300c2 100 break;
101 }
102 return ret;
103}
104
396c147e 105static int
b5e300c2 106flags_from_portable(int pflags)
107{
108 int flags = 0;
dce9bac5 109
79ddf6db 110 if ((pflags & SSH2_FXF_READ) &&
111 (pflags & SSH2_FXF_WRITE)) {
b5e300c2 112 flags = O_RDWR;
f546c780 113 } else if (pflags & SSH2_FXF_READ) {
b5e300c2 114 flags = O_RDONLY;
f546c780 115 } else if (pflags & SSH2_FXF_WRITE) {
b5e300c2 116 flags = O_WRONLY;
117 }
f546c780 118 if (pflags & SSH2_FXF_CREAT)
b5e300c2 119 flags |= O_CREAT;
f546c780 120 if (pflags & SSH2_FXF_TRUNC)
b5e300c2 121 flags |= O_TRUNC;
f546c780 122 if (pflags & SSH2_FXF_EXCL)
b5e300c2 123 flags |= O_EXCL;
124 return flags;
125}
126
a13880bb 127static const char *
128string_from_portable(int pflags)
129{
130 static char ret[128];
131
132 *ret = '\0';
133
134#define PAPPEND(str) { \
135 if (*ret != '\0') \
136 strlcat(ret, ",", sizeof(ret)); \
137 strlcat(ret, str, sizeof(ret)); \
138 }
139
140 if (pflags & SSH2_FXF_READ)
141 PAPPEND("READ")
142 if (pflags & SSH2_FXF_WRITE)
143 PAPPEND("WRITE")
144 if (pflags & SSH2_FXF_CREAT)
145 PAPPEND("CREATE")
146 if (pflags & SSH2_FXF_TRUNC)
147 PAPPEND("TRUNCATE")
148 if (pflags & SSH2_FXF_EXCL)
149 PAPPEND("EXCL")
150
151 return ret;
152}
153
396c147e 154static Attrib *
b5e300c2 155get_attrib(void)
156{
157 return decode_attrib(&iqueue);
158}
159
160/* handle handles */
161
162typedef struct Handle Handle;
163struct Handle {
164 int use;
165 DIR *dirp;
166 int fd;
167 char *name;
a13880bb 168 u_int64_t bytes_read, bytes_write;
b5e300c2 169};
dce9bac5 170
b5e300c2 171enum {
172 HANDLE_UNUSED,
173 HANDLE_DIR,
174 HANDLE_FILE
175};
dce9bac5 176
b5e300c2 177Handle handles[100];
178
396c147e 179static void
b5e300c2 180handle_init(void)
181{
2ceb8101 182 u_int i;
dce9bac5 183
184eed6a 184 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
b5e300c2 185 handles[i].use = HANDLE_UNUSED;
186}
187
396c147e 188static int
b6c7b7b7 189handle_new(int use, const char *name, int fd, DIR *dirp)
b5e300c2 190{
2ceb8101 191 u_int i;
dce9bac5 192
184eed6a 193 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
b5e300c2 194 if (handles[i].use == HANDLE_UNUSED) {
195 handles[i].use = use;
196 handles[i].dirp = dirp;
197 handles[i].fd = fd;
3e2f2431 198 handles[i].name = xstrdup(name);
a13880bb 199 handles[i].bytes_read = handles[i].bytes_write = 0;
b5e300c2 200 return i;
201 }
202 }
203 return -1;
204}
205
396c147e 206static int
b5e300c2 207handle_is_ok(int i, int type)
208{
2ceb8101 209 return i >= 0 && (u_int)i < sizeof(handles)/sizeof(Handle) &&
f546c780 210 handles[i].use == type;
b5e300c2 211}
212
396c147e 213static int
b5e300c2 214handle_to_string(int handle, char **stringp, int *hlenp)
215{
b5e300c2 216 if (stringp == NULL || hlenp == NULL)
217 return -1;
b5c334cc 218 *stringp = xmalloc(sizeof(int32_t));
51e7a012 219 put_u32(*stringp, handle);
b5c334cc 220 *hlenp = sizeof(int32_t);
b5e300c2 221 return 0;
222}
223
396c147e 224static int
b6c7b7b7 225handle_from_string(const char *handle, u_int hlen)
b5e300c2 226{
b5c334cc 227 int val;
dce9bac5 228
b5c334cc 229 if (hlen != sizeof(int32_t))
b5e300c2 230 return -1;
51e7a012 231 val = get_u32(handle);
b5e300c2 232 if (handle_is_ok(val, HANDLE_FILE) ||
233 handle_is_ok(val, HANDLE_DIR))
234 return val;
235 return -1;
236}
237
396c147e 238static char *
b5e300c2 239handle_to_name(int handle)
240{
241 if (handle_is_ok(handle, HANDLE_DIR)||
242 handle_is_ok(handle, HANDLE_FILE))
243 return handles[handle].name;
244 return NULL;
245}
246
396c147e 247static DIR *
b5e300c2 248handle_to_dir(int handle)
249{
250 if (handle_is_ok(handle, HANDLE_DIR))
251 return handles[handle].dirp;
252 return NULL;
253}
254
396c147e 255static int
b5e300c2 256handle_to_fd(int handle)
257{
2b87da3b 258 if (handle_is_ok(handle, HANDLE_FILE))
b5e300c2 259 return handles[handle].fd;
260 return -1;
261}
262
a13880bb 263static void
264handle_update_read(int handle, ssize_t bytes)
265{
266 if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0)
267 handles[handle].bytes_read += bytes;
268}
269
270static void
271handle_update_write(int handle, ssize_t bytes)
272{
273 if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0)
274 handles[handle].bytes_write += bytes;
275}
276
277static u_int64_t
278handle_bytes_read(int handle)
279{
280 if (handle_is_ok(handle, HANDLE_FILE))
281 return (handles[handle].bytes_read);
282 return 0;
283}
284
285static u_int64_t
286handle_bytes_write(int handle)
287{
288 if (handle_is_ok(handle, HANDLE_FILE))
289 return (handles[handle].bytes_write);
290 return 0;
291}
292
396c147e 293static int
b5e300c2 294handle_close(int handle)
295{
296 int ret = -1;
dce9bac5 297
b5e300c2 298 if (handle_is_ok(handle, HANDLE_FILE)) {
299 ret = close(handles[handle].fd);
300 handles[handle].use = HANDLE_UNUSED;
3e2f2431 301 xfree(handles[handle].name);
b5e300c2 302 } else if (handle_is_ok(handle, HANDLE_DIR)) {
303 ret = closedir(handles[handle].dirp);
304 handles[handle].use = HANDLE_UNUSED;
3e2f2431 305 xfree(handles[handle].name);
b5e300c2 306 } else {
307 errno = ENOENT;
308 }
309 return ret;
310}
311
a13880bb 312static void
313handle_log_close(int handle, char *emsg)
314{
315 if (handle_is_ok(handle, HANDLE_FILE)) {
316 logit("%s%sclose \"%s\" bytes read %llu written %llu",
317 emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ",
318 handle_to_name(handle),
319 handle_bytes_read(handle), handle_bytes_write(handle));
320 } else {
321 logit("%s%sclosedir \"%s\"",
322 emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ",
323 handle_to_name(handle));
324 }
325}
326
327static void
328handle_log_exit(void)
329{
330 u_int i;
331
332 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
333 if (handles[i].use != HANDLE_UNUSED)
334 handle_log_close(i, "forced");
335}
336
396c147e 337static int
b5e300c2 338get_handle(void)
339{
340 char *handle;
f546c780 341 int val = -1;
bcbf86ec 342 u_int hlen;
dce9bac5 343
b5e300c2 344 handle = get_string(&hlen);
f546c780 345 if (hlen < 256)
346 val = handle_from_string(handle, hlen);
b5e300c2 347 xfree(handle);
348 return val;
349}
350
351/* send replies */
352
396c147e 353static void
b5e300c2 354send_msg(Buffer *m)
355{
356 int mlen = buffer_len(m);
dce9bac5 357
b5e300c2 358 buffer_put_int(&oqueue, mlen);
359 buffer_append(&oqueue, buffer_ptr(m), mlen);
360 buffer_consume(m, mlen);
361}
362
a13880bb 363static const char *
364status_to_message(u_int32_t status)
b5e300c2 365{
3a7fe5ba 366 const char *status_messages[] = {
367 "Success", /* SSH_FX_OK */
368 "End of file", /* SSH_FX_EOF */
369 "No such file", /* SSH_FX_NO_SUCH_FILE */
370 "Permission denied", /* SSH_FX_PERMISSION_DENIED */
371 "Failure", /* SSH_FX_FAILURE */
372 "Bad message", /* SSH_FX_BAD_MESSAGE */
373 "No connection", /* SSH_FX_NO_CONNECTION */
374 "Connection lost", /* SSH_FX_CONNECTION_LOST */
375 "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */
376 "Unknown error" /* Others */
377 };
a13880bb 378 return (status_messages[MIN(status,SSH2_FX_MAX)]);
379}
dce9bac5 380
a13880bb 381static void
382send_status(u_int32_t id, u_int32_t status)
383{
384 Buffer msg;
385
386 debug3("request %u: sent status %u", id, status);
387 if (log_level > SYSLOG_LEVEL_VERBOSE ||
388 (status != SSH2_FX_OK && status != SSH2_FX_EOF))
389 logit("sent status %s", status_to_message(status));
b5e300c2 390 buffer_init(&msg);
f546c780 391 buffer_put_char(&msg, SSH2_FXP_STATUS);
b5e300c2 392 buffer_put_int(&msg, id);
ca75d7de 393 buffer_put_int(&msg, status);
3a7fe5ba 394 if (version >= 3) {
a13880bb 395 buffer_put_cstring(&msg, status_to_message(status));
3a7fe5ba 396 buffer_put_cstring(&msg, "");
397 }
b5e300c2 398 send_msg(&msg);
399 buffer_free(&msg);
400}
396c147e 401static void
b6c7b7b7 402send_data_or_handle(char type, u_int32_t id, const char *data, int dlen)
b5e300c2 403{
404 Buffer msg;
dce9bac5 405
b5e300c2 406 buffer_init(&msg);
407 buffer_put_char(&msg, type);
408 buffer_put_int(&msg, id);
409 buffer_put_string(&msg, data, dlen);
410 send_msg(&msg);
411 buffer_free(&msg);
412}
413
396c147e 414static void
b6c7b7b7 415send_data(u_int32_t id, const char *data, int dlen)
b5e300c2 416{
a13880bb 417 debug("request %u: sent data len %d", id, dlen);
f546c780 418 send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
b5e300c2 419}
420
396c147e 421static void
b5e300c2 422send_handle(u_int32_t id, int handle)
423{
424 char *string;
425 int hlen;
dce9bac5 426
b5e300c2 427 handle_to_string(handle, &string, &hlen);
a13880bb 428 debug("request %u: sent handle handle %d", id, handle);
f546c780 429 send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
b5e300c2 430 xfree(string);
431}
432
396c147e 433static void
b6c7b7b7 434send_names(u_int32_t id, int count, const Stat *stats)
b5e300c2 435{
436 Buffer msg;
437 int i;
dce9bac5 438
b5e300c2 439 buffer_init(&msg);
f546c780 440 buffer_put_char(&msg, SSH2_FXP_NAME);
b5e300c2 441 buffer_put_int(&msg, id);
442 buffer_put_int(&msg, count);
a13880bb 443 debug("request %u: sent names count %d", id, count);
b5e300c2 444 for (i = 0; i < count; i++) {
445 buffer_put_cstring(&msg, stats[i].name);
446 buffer_put_cstring(&msg, stats[i].long_name);
447 encode_attrib(&msg, &stats[i].attrib);
448 }
449 send_msg(&msg);
450 buffer_free(&msg);
451}
452
396c147e 453static void
b6c7b7b7 454send_attrib(u_int32_t id, const Attrib *a)
b5e300c2 455{
456 Buffer msg;
dce9bac5 457
a13880bb 458 debug("request %u: sent attrib have 0x%x", id, a->flags);
b5e300c2 459 buffer_init(&msg);
f546c780 460 buffer_put_char(&msg, SSH2_FXP_ATTRS);
b5e300c2 461 buffer_put_int(&msg, id);
462 encode_attrib(&msg, a);
463 send_msg(&msg);
464 buffer_free(&msg);
465}
466
467/* parse incoming */
468
396c147e 469static void
b5e300c2 470process_init(void)
471{
472 Buffer msg;
b5e300c2 473
802d93bb 474 version = get_int();
a13880bb 475 verbose("received client version %d", version);
b5e300c2 476 buffer_init(&msg);
f546c780 477 buffer_put_char(&msg, SSH2_FXP_VERSION);
478 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
b5e300c2 479 send_msg(&msg);
480 buffer_free(&msg);
481}
482
396c147e 483static void
b5e300c2 484process_open(void)
485{
486 u_int32_t id, pflags;
487 Attrib *a;
488 char *name;
f546c780 489 int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
b5e300c2 490
491 id = get_int();
492 name = get_string(NULL);
f546c780 493 pflags = get_int(); /* portable flags */
26ddd377 494 debug3("request %u: open flags %d", id, pflags);
b5e300c2 495 a = get_attrib();
496 flags = flags_from_portable(pflags);
f546c780 497 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
a13880bb 498 logit("open \"%s\" flags %s mode 0%o",
499 name, string_from_portable(pflags), mode);
b5e300c2 500 fd = open(name, flags, mode);
501 if (fd < 0) {
502 status = errno_to_portable(errno);
503 } else {
3e2f2431 504 handle = handle_new(HANDLE_FILE, name, fd, NULL);
b5e300c2 505 if (handle < 0) {
506 close(fd);
507 } else {
508 send_handle(id, handle);
f546c780 509 status = SSH2_FX_OK;
b5e300c2 510 }
511 }
f546c780 512 if (status != SSH2_FX_OK)
b5e300c2 513 send_status(id, status);
514 xfree(name);
515}
516
396c147e 517static void
b5e300c2 518process_close(void)
519{
520 u_int32_t id;
f546c780 521 int handle, ret, status = SSH2_FX_FAILURE;
b5e300c2 522
523 id = get_int();
524 handle = get_handle();
a13880bb 525 debug3("request %u: close handle %u", id, handle);
526 handle_log_close(handle, NULL);
b5e300c2 527 ret = handle_close(handle);
f546c780 528 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
b5e300c2 529 send_status(id, status);
530}
531
396c147e 532static void
b5e300c2 533process_read(void)
534{
535 char buf[64*1024];
f546c780 536 u_int32_t id, len;
537 int handle, fd, ret, status = SSH2_FX_FAILURE;
b5e300c2 538 u_int64_t off;
539
540 id = get_int();
541 handle = get_handle();
f546c780 542 off = get_int64();
b5e300c2 543 len = get_int();
544
a13880bb 545 debug("request %u: read \"%s\" (handle %d) off %llu len %d",
546 id, handle_to_name(handle), handle, (unsigned long long)off, len);
b5e300c2 547 if (len > sizeof buf) {
548 len = sizeof buf;
a13880bb 549 debug2("read change len %d", len);
b5e300c2 550 }
551 fd = handle_to_fd(handle);
552 if (fd >= 0) {
553 if (lseek(fd, off, SEEK_SET) < 0) {
554 error("process_read: seek failed");
555 status = errno_to_portable(errno);
556 } else {
557 ret = read(fd, buf, len);
558 if (ret < 0) {
559 status = errno_to_portable(errno);
560 } else if (ret == 0) {
f546c780 561 status = SSH2_FX_EOF;
b5e300c2 562 } else {
563 send_data(id, buf, ret);
f546c780 564 status = SSH2_FX_OK;
a13880bb 565 handle_update_read(handle, ret);
b5e300c2 566 }
567 }
568 }
f546c780 569 if (status != SSH2_FX_OK)
b5e300c2 570 send_status(id, status);
571}
572
396c147e 573static void
b5e300c2 574process_write(void)
575{
f546c780 576 u_int32_t id;
b5e300c2 577 u_int64_t off;
bcbf86ec 578 u_int len;
f546c780 579 int handle, fd, ret, status = SSH2_FX_FAILURE;
b5e300c2 580 char *data;
581
582 id = get_int();
583 handle = get_handle();
f546c780 584 off = get_int64();
b5e300c2 585 data = get_string(&len);
586
a13880bb 587 debug("request %u: write \"%s\" (handle %d) off %llu len %d",
588 id, handle_to_name(handle), handle, (unsigned long long)off, len);
b5e300c2 589 fd = handle_to_fd(handle);
590 if (fd >= 0) {
591 if (lseek(fd, off, SEEK_SET) < 0) {
592 status = errno_to_portable(errno);
593 error("process_write: seek failed");
594 } else {
595/* XXX ATOMICIO ? */
596 ret = write(fd, data, len);
2ceb8101 597 if (ret < 0) {
b5e300c2 598 error("process_write: write failed");
599 status = errno_to_portable(errno);
2ceb8101 600 } else if ((size_t)ret == len) {
f546c780 601 status = SSH2_FX_OK;
a13880bb 602 handle_update_write(handle, ret);
b5e300c2 603 } else {
a13880bb 604 debug2("nothing at all written");
b5e300c2 605 }
606 }
607 }
608 send_status(id, status);
609 xfree(data);
610}
611
396c147e 612static void
b5e300c2 613process_do_stat(int do_lstat)
614{
b5c334cc 615 Attrib a;
b5e300c2 616 struct stat st;
617 u_int32_t id;
618 char *name;
f546c780 619 int ret, status = SSH2_FX_FAILURE;
b5e300c2 620
621 id = get_int();
622 name = get_string(NULL);
a13880bb 623 debug3("request %u: %sstat", id, do_lstat ? "l" : "");
624 verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name);
b5e300c2 625 ret = do_lstat ? lstat(name, &st) : stat(name, &st);
626 if (ret < 0) {
627 status = errno_to_portable(errno);
628 } else {
b5c334cc 629 stat_to_attrib(&st, &a);
630 send_attrib(id, &a);
f546c780 631 status = SSH2_FX_OK;
b5e300c2 632 }
f546c780 633 if (status != SSH2_FX_OK)
b5e300c2 634 send_status(id, status);
635 xfree(name);
636}
637
396c147e 638static void
b5e300c2 639process_stat(void)
640{
641 process_do_stat(0);
642}
643
396c147e 644static void
b5e300c2 645process_lstat(void)
646{
647 process_do_stat(1);
648}
649
396c147e 650static void
b5e300c2 651process_fstat(void)
652{
b5c334cc 653 Attrib a;
b5e300c2 654 struct stat st;
655 u_int32_t id;
f546c780 656 int fd, ret, handle, status = SSH2_FX_FAILURE;
b5e300c2 657
658 id = get_int();
659 handle = get_handle();
a13880bb 660 debug("request %u: fstat \"%s\" (handle %u)",
661 id, handle_to_name(handle), handle);
b5e300c2 662 fd = handle_to_fd(handle);
663 if (fd >= 0) {
664 ret = fstat(fd, &st);
665 if (ret < 0) {
666 status = errno_to_portable(errno);
667 } else {
b5c334cc 668 stat_to_attrib(&st, &a);
669 send_attrib(id, &a);
f546c780 670 status = SSH2_FX_OK;
b5e300c2 671 }
672 }
f546c780 673 if (status != SSH2_FX_OK)
b5e300c2 674 send_status(id, status);
675}
676
396c147e 677static struct timeval *
b6c7b7b7 678attrib_to_tv(const Attrib *a)
b5e300c2 679{
680 static struct timeval tv[2];
dce9bac5 681
b5e300c2 682 tv[0].tv_sec = a->atime;
683 tv[0].tv_usec = 0;
684 tv[1].tv_sec = a->mtime;
685 tv[1].tv_usec = 0;
686 return tv;
687}
688
396c147e 689static void
b5e300c2 690process_setstat(void)
691{
692 Attrib *a;
693 u_int32_t id;
694 char *name;
9906a836 695 int status = SSH2_FX_OK, ret;
b5e300c2 696
697 id = get_int();
698 name = get_string(NULL);
699 a = get_attrib();
a13880bb 700 debug("request %u: setstat name \"%s\"", id, name);
cb476289 701 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
a13880bb 702 logit("set \"%s\" size %llu", name, a->size);
cb476289 703 ret = truncate(name, a->size);
704 if (ret == -1)
705 status = errno_to_portable(errno);
706 }
f546c780 707 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
a13880bb 708 logit("set \"%s\" mode %04o", name, a->perm);
b5e300c2 709 ret = chmod(name, a->perm & 0777);
710 if (ret == -1)
711 status = errno_to_portable(errno);
712 }
f546c780 713 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
a13880bb 714 char buf[64];
715 time_t t = a->mtime;
716
717 strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S",
718 localtime(&t));
719 logit("set \"%s\" modtime %s", name, buf);
b5e300c2 720 ret = utimes(name, attrib_to_tv(a));
721 if (ret == -1)
722 status = errno_to_portable(errno);
723 }
408ba72f 724 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
a13880bb 725 logit("set \"%s\" owner %lu group %lu", name,
726 (u_long)a->uid, (u_long)a->gid);
408ba72f 727 ret = chown(name, a->uid, a->gid);
728 if (ret == -1)
729 status = errno_to_portable(errno);
730 }
b5e300c2 731 send_status(id, status);
732 xfree(name);
733}
734
396c147e 735static void
b5e300c2 736process_fsetstat(void)
737{
738 Attrib *a;
739 u_int32_t id;
740 int handle, fd, ret;
f546c780 741 int status = SSH2_FX_OK;
16e538d4 742
b5e300c2 743 id = get_int();
744 handle = get_handle();
745 a = get_attrib();
a13880bb 746 debug("request %u: fsetstat handle %d", id, handle);
b5e300c2 747 fd = handle_to_fd(handle);
a13880bb 748 if (fd < 0) {
f546c780 749 status = SSH2_FX_FAILURE;
b5e300c2 750 } else {
a13880bb 751 char *name = handle_to_name(handle);
752
cb476289 753 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
a13880bb 754 logit("set \"%s\" size %llu", name, a->size);
cb476289 755 ret = ftruncate(fd, a->size);
756 if (ret == -1)
757 status = errno_to_portable(errno);
758 }
f546c780 759 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
a13880bb 760 logit("set \"%s\" mode %04o", name, a->perm);
2fd3c144 761#ifdef HAVE_FCHMOD
b5e300c2 762 ret = fchmod(fd, a->perm & 0777);
2fd3c144 763#else
c33f0b36 764 ret = chmod(name, a->perm & 0777);
2fd3c144 765#endif
b5e300c2 766 if (ret == -1)
767 status = errno_to_portable(errno);
768 }
f546c780 769 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
a13880bb 770 char buf[64];
771 time_t t = a->mtime;
772
773 strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S",
774 localtime(&t));
775 logit("set \"%s\" modtime %s", name, buf);
b5e300c2 776#ifdef HAVE_FUTIMES
777 ret = futimes(fd, attrib_to_tv(a));
778#else
779 ret = utimes(name, attrib_to_tv(a));
780#endif
781 if (ret == -1)
782 status = errno_to_portable(errno);
783 }
408ba72f 784 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
a13880bb 785 logit("set \"%s\" owner %lu group %lu", name,
786 (u_long)a->uid, (u_long)a->gid);
01f13020 787#ifdef HAVE_FCHOWN
408ba72f 788 ret = fchown(fd, a->uid, a->gid);
01f13020 789#else
790 ret = chown(name, a->uid, a->gid);
791#endif
408ba72f 792 if (ret == -1)
793 status = errno_to_portable(errno);
794 }
b5e300c2 795 }
796 send_status(id, status);
797}
798
396c147e 799static void
b5e300c2 800process_opendir(void)
801{
802 DIR *dirp = NULL;
803 char *path;
f546c780 804 int handle, status = SSH2_FX_FAILURE;
b5e300c2 805 u_int32_t id;
806
807 id = get_int();
808 path = get_string(NULL);
a13880bb 809 debug3("request %u: opendir", id);
810 logit("opendir \"%s\"", path);
2b87da3b 811 dirp = opendir(path);
b5e300c2 812 if (dirp == NULL) {
813 status = errno_to_portable(errno);
814 } else {
3e2f2431 815 handle = handle_new(HANDLE_DIR, path, 0, dirp);
b5e300c2 816 if (handle < 0) {
817 closedir(dirp);
818 } else {
819 send_handle(id, handle);
f546c780 820 status = SSH2_FX_OK;
b5e300c2 821 }
2b87da3b 822
b5e300c2 823 }
f546c780 824 if (status != SSH2_FX_OK)
b5e300c2 825 send_status(id, status);
826 xfree(path);
827}
828
396c147e 829static void
b5e300c2 830process_readdir(void)
831{
832 DIR *dirp;
833 struct dirent *dp;
834 char *path;
835 int handle;
836 u_int32_t id;
837
838 id = get_int();
839 handle = get_handle();
a13880bb 840 debug("request %u: readdir \"%s\" (handle %d)", id,
841 handle_to_name(handle), handle);
b5e300c2 842 dirp = handle_to_dir(handle);
843 path = handle_to_name(handle);
844 if (dirp == NULL || path == NULL) {
f546c780 845 send_status(id, SSH2_FX_FAILURE);
b5e300c2 846 } else {
b5e300c2 847 struct stat st;
a13880bb 848 char pathname[MAXPATHLEN];
b5e300c2 849 Stat *stats;
850 int nstats = 10, count = 0, i;
9906a836 851
52e3daed 852 stats = xcalloc(nstats, sizeof(Stat));
b5e300c2 853 while ((dp = readdir(dirp)) != NULL) {
854 if (count >= nstats) {
855 nstats *= 2;
c5d10563 856 stats = xrealloc(stats, nstats, sizeof(Stat));
b5e300c2 857 }
858/* XXX OVERFLOW ? */
88690211 859 snprintf(pathname, sizeof pathname, "%s%s%s", path,
860 strcmp(path, "/") ? "/" : "", dp->d_name);
b5e300c2 861 if (lstat(pathname, &st) < 0)
862 continue;
b5c334cc 863 stat_to_attrib(&st, &(stats[count].attrib));
b5e300c2 864 stats[count].name = xstrdup(dp->d_name);
00b3ad3e 865 stats[count].long_name = ls_file(dp->d_name, &st, 0);
b5e300c2 866 count++;
867 /* send up to 100 entries in one message */
b5c334cc 868 /* XXX check packet size instead */
b5e300c2 869 if (count == 100)
870 break;
871 }
f546c780 872 if (count > 0) {
873 send_names(id, count, stats);
184eed6a 874 for (i = 0; i < count; i++) {
f546c780 875 xfree(stats[i].name);
876 xfree(stats[i].long_name);
877 }
878 } else {
879 send_status(id, SSH2_FX_EOF);
b5e300c2 880 }
881 xfree(stats);
882 }
883}
884
396c147e 885static void
b5e300c2 886process_remove(void)
887{
888 char *name;
889 u_int32_t id;
f546c780 890 int status = SSH2_FX_FAILURE;
b5e300c2 891 int ret;
892
893 id = get_int();
894 name = get_string(NULL);
a13880bb 895 debug3("request %u: remove", id);
896 logit("remove name \"%s\"", name);
67b0facb 897 ret = unlink(name);
f546c780 898 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
b5e300c2 899 send_status(id, status);
900 xfree(name);
901}
902
396c147e 903static void
b5e300c2 904process_mkdir(void)
905{
906 Attrib *a;
907 u_int32_t id;
908 char *name;
f546c780 909 int ret, mode, status = SSH2_FX_FAILURE;
b5e300c2 910
911 id = get_int();
912 name = get_string(NULL);
913 a = get_attrib();
f546c780 914 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
915 a->perm & 0777 : 0777;
a13880bb 916 debug3("request %u: mkdir", id);
917 logit("mkdir name \"%s\" mode 0%o", name, mode);
b5e300c2 918 ret = mkdir(name, mode);
f546c780 919 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
b5e300c2 920 send_status(id, status);
921 xfree(name);
922}
923
396c147e 924static void
b5e300c2 925process_rmdir(void)
926{
927 u_int32_t id;
928 char *name;
929 int ret, status;
930
931 id = get_int();
932 name = get_string(NULL);
a13880bb 933 debug3("request %u: rmdir", id);
934 logit("rmdir name \"%s\"", name);
b5e300c2 935 ret = rmdir(name);
f546c780 936 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
b5e300c2 937 send_status(id, status);
938 xfree(name);
939}
940
396c147e 941static void
b5e300c2 942process_realpath(void)
943{
944 char resolvedname[MAXPATHLEN];
945 u_int32_t id;
946 char *path;
947
948 id = get_int();
949 path = get_string(NULL);
6b523bae 950 if (path[0] == '\0') {
951 xfree(path);
952 path = xstrdup(".");
953 }
a13880bb 954 debug3("request %u: realpath", id);
955 verbose("realpath \"%s\"", path);
b5e300c2 956 if (realpath(path, resolvedname) == NULL) {
957 send_status(id, errno_to_portable(errno));
958 } else {
959 Stat s;
960 attrib_clear(&s.attrib);
961 s.name = s.long_name = resolvedname;
962 send_names(id, 1, &s);
963 }
964 xfree(path);
965}
966
396c147e 967static void
b5e300c2 968process_rename(void)
969{
970 u_int32_t id;
971 char *oldpath, *newpath;
f2f28f1f 972 int status;
fd77a40f 973 struct stat sb;
b5e300c2 974
975 id = get_int();
976 oldpath = get_string(NULL);
977 newpath = get_string(NULL);
a13880bb 978 debug3("request %u: rename", id);
979 logit("rename old \"%s\" new \"%s\"", oldpath, newpath);
fd77a40f 980 status = SSH2_FX_FAILURE;
981 if (lstat(oldpath, &sb) == -1)
f2f28f1f 982 status = errno_to_portable(errno);
fd77a40f 983 else if (S_ISREG(sb.st_mode)) {
984 /* Race-free rename of regular files */
dc5888bf 985 if (link(oldpath, newpath) == -1) {
cd698186 986 if (errno == EOPNOTSUPP
987#ifdef LINK_OPNOTSUPP_ERRNO
988 || errno == LINK_OPNOTSUPP_ERRNO
989#endif
990 ) {
dc5888bf 991 struct stat st;
992
993 /*
994 * fs doesn't support links, so fall back to
995 * stat+rename. This is racy.
996 */
997 if (stat(newpath, &st) == -1) {
998 if (rename(oldpath, newpath) == -1)
999 status =
1000 errno_to_portable(errno);
1001 else
1002 status = SSH2_FX_OK;
1003 }
1004 } else {
1005 status = errno_to_portable(errno);
1006 }
1007 } else if (unlink(oldpath) == -1) {
fd77a40f 1008 status = errno_to_portable(errno);
1009 /* clean spare link */
1010 unlink(newpath);
1011 } else
1012 status = SSH2_FX_OK;
1013 } else if (stat(newpath, &sb) == -1) {
1014 if (rename(oldpath, newpath) == -1)
1015 status = errno_to_portable(errno);
1016 else
1017 status = SSH2_FX_OK;
1018 }
b5e300c2 1019 send_status(id, status);
1020 xfree(oldpath);
1021 xfree(newpath);
1022}
1023
396c147e 1024static void
3a7fe5ba 1025process_readlink(void)
1026{
1027 u_int32_t id;
362df52e 1028 int len;
ca75d7de 1029 char buf[MAXPATHLEN];
3a7fe5ba 1030 char *path;
1031
1032 id = get_int();
1033 path = get_string(NULL);
a13880bb 1034 debug3("request %u: readlink", id);
1035 verbose("readlink \"%s\"", path);
ca75d7de 1036 if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1)
3a7fe5ba 1037 send_status(id, errno_to_portable(errno));
1038 else {
1039 Stat s;
184eed6a 1040
ca75d7de 1041 buf[len] = '\0';
3a7fe5ba 1042 attrib_clear(&s.attrib);
ca75d7de 1043 s.name = s.long_name = buf;
3a7fe5ba 1044 send_names(id, 1, &s);
1045 }
1046 xfree(path);
1047}
1048
396c147e 1049static void
3a7fe5ba 1050process_symlink(void)
1051{
1052 u_int32_t id;
3a7fe5ba 1053 char *oldpath, *newpath;
f2f28f1f 1054 int ret, status;
3a7fe5ba 1055
1056 id = get_int();
1057 oldpath = get_string(NULL);
1058 newpath = get_string(NULL);
a13880bb 1059 debug3("request %u: symlink", id);
1060 logit("symlink old \"%s\" new \"%s\"", oldpath, newpath);
f2f28f1f 1061 /* this will fail if 'newpath' exists */
1062 ret = symlink(oldpath, newpath);
1063 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
3a7fe5ba 1064 send_status(id, status);
1065 xfree(oldpath);
1066 xfree(newpath);
1067}
1068
396c147e 1069static void
f546c780 1070process_extended(void)
1071{
1072 u_int32_t id;
1073 char *request;
1074
1075 id = get_int();
1076 request = get_string(NULL);
1077 send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
1078 xfree(request);
1079}
b5e300c2 1080
1081/* stolen from ssh-agent */
1082
396c147e 1083static void
b5e300c2 1084process(void)
1085{
1e3b8b07 1086 u_int msg_len;
9b4ac641 1087 u_int buf_len;
1088 u_int consumed;
1e3b8b07 1089 u_int type;
1090 u_char *cp;
b5e300c2 1091
9b4ac641 1092 buf_len = buffer_len(&iqueue);
1093 if (buf_len < 5)
b5e300c2 1094 return; /* Incomplete message. */
20905a8e 1095 cp = buffer_ptr(&iqueue);
51e7a012 1096 msg_len = get_u32(cp);
3eee3b86 1097 if (msg_len > SFTP_MAX_MSG_LENGTH) {
a13880bb 1098 error("bad message from %s local user %s",
1099 client_addr, pw->pw_name);
1100 cleanup_exit(11);
b5e300c2 1101 }
9b4ac641 1102 if (buf_len < msg_len + 4)
b5e300c2 1103 return;
1104 buffer_consume(&iqueue, 4);
9b4ac641 1105 buf_len -= 4;
b5e300c2 1106 type = buffer_get_char(&iqueue);
1107 switch (type) {
f546c780 1108 case SSH2_FXP_INIT:
b5e300c2 1109 process_init();
1110 break;
f546c780 1111 case SSH2_FXP_OPEN:
b5e300c2 1112 process_open();
1113 break;
f546c780 1114 case SSH2_FXP_CLOSE:
b5e300c2 1115 process_close();
1116 break;
f546c780 1117 case SSH2_FXP_READ:
b5e300c2 1118 process_read();
1119 break;
f546c780 1120 case SSH2_FXP_WRITE:
b5e300c2 1121 process_write();
1122 break;
f546c780 1123 case SSH2_FXP_LSTAT:
b5e300c2 1124 process_lstat();
1125 break;
f546c780 1126 case SSH2_FXP_FSTAT:
b5e300c2 1127 process_fstat();
1128 break;
f546c780 1129 case SSH2_FXP_SETSTAT:
b5e300c2 1130 process_setstat();
1131 break;
f546c780 1132 case SSH2_FXP_FSETSTAT:
b5e300c2 1133 process_fsetstat();
1134 break;
f546c780 1135 case SSH2_FXP_OPENDIR:
b5e300c2 1136 process_opendir();
1137 break;
f546c780 1138 case SSH2_FXP_READDIR:
b5e300c2 1139 process_readdir();
1140 break;
f546c780 1141 case SSH2_FXP_REMOVE:
b5e300c2 1142 process_remove();
1143 break;
f546c780 1144 case SSH2_FXP_MKDIR:
b5e300c2 1145 process_mkdir();
1146 break;
f546c780 1147 case SSH2_FXP_RMDIR:
b5e300c2 1148 process_rmdir();
1149 break;
f546c780 1150 case SSH2_FXP_REALPATH:
b5e300c2 1151 process_realpath();
1152 break;
f546c780 1153 case SSH2_FXP_STAT:
b5e300c2 1154 process_stat();
1155 break;
f546c780 1156 case SSH2_FXP_RENAME:
b5e300c2 1157 process_rename();
1158 break;
3a7fe5ba 1159 case SSH2_FXP_READLINK:
1160 process_readlink();
1161 break;
1162 case SSH2_FXP_SYMLINK:
1163 process_symlink();
1164 break;
f546c780 1165 case SSH2_FXP_EXTENDED:
1166 process_extended();
1167 break;
b5e300c2 1168 default:
1169 error("Unknown message %d", type);
1170 break;
1171 }
9b4ac641 1172 /* discard the remaining bytes from the current packet */
1173 if (buf_len < buffer_len(&iqueue))
a13880bb 1174 fatal("iqueue grew unexpectedly");
9b4ac641 1175 consumed = buf_len - buffer_len(&iqueue);
1176 if (msg_len < consumed)
1177 fatal("msg_len %d < consumed %d", msg_len, consumed);
1178 if (msg_len > consumed)
1179 buffer_consume(&iqueue, msg_len - consumed);
b5e300c2 1180}
1181
a13880bb 1182/* Cleanup handler that logs active handles upon normal exit */
1183void
1184cleanup_exit(int i)
1185{
1186 if (pw != NULL && client_addr != NULL) {
1187 handle_log_exit();
1188 logit("session closed for local user %s from [%s]",
1189 pw->pw_name, client_addr);
1190 }
1191 _exit(i);
1192}
1193
1194static void
1195usage(void)
1196{
1197 extern char *__progname;
1198
1199 fprintf(stderr,
1200 "usage: %s [-he] [-l log_level] [-f log_facility]\n", __progname);
1201 exit(1);
1202}
1203
b5e300c2 1204int
a13880bb 1205main(int argc, char **argv)
b5e300c2 1206{
c8d75031 1207 fd_set *rset, *wset;
a13880bb 1208 int in, out, max, ch, skipargs = 0, log_stderr = 0;
c8d75031 1209 ssize_t len, olen, set_size;
a13880bb 1210 SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
1211 char *cp;
1212
a13880bb 1213 extern char *optarg;
1214 extern char *__progname;
b5e300c2 1215
fd6168c1 1216 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1217 sanitise_stdfd();
1218
a13880bb 1219 __progname = ssh_get_progname(argv[0]);
1220 log_init(__progname, log_level, log_facility, log_stderr);
1221
1222 while (!skipargs && (ch = getopt(argc, argv, "C:f:l:che")) != -1) {
1223 switch (ch) {
1224 case 'c':
1225 /*
1226 * Ignore all arguments if we are invoked as a
1227 * shell using "sftp-server -c command"
1228 */
1229 skipargs = 1;
1230 break;
1231 case 'e':
1232 log_stderr = 1;
1233 break;
1234 case 'l':
1235 log_level = log_level_number(optarg);
1236 if (log_level == SYSLOG_LEVEL_NOT_SET)
1237 error("Invalid log level \"%s\"", optarg);
1238 break;
1239 case 'f':
1240 log_facility = log_facility_number(optarg);
1241 if (log_level == SYSLOG_FACILITY_NOT_SET)
1242 error("Invalid log facility \"%s\"", optarg);
1243 break;
1244 case 'h':
1245 default:
1246 usage();
1247 }
1248 }
61b3a2bc 1249
a13880bb 1250 log_init(__progname, log_level, log_facility, log_stderr);
b5e300c2 1251
a13880bb 1252 if ((cp = getenv("SSH_CONNECTION")) != NULL) {
1253 client_addr = xstrdup(cp);
1254 if ((cp = strchr(client_addr, ' ')) == NULL)
1255 fatal("Malformed SSH_CONNECTION variable: \"%s\"",
1256 getenv("SSH_CONNECTION"));
1257 *cp = '\0';
1258 } else
1259 client_addr = xstrdup("UNKNOWN");
1260
1261 if ((pw = getpwuid(getuid())) == NULL)
1262 fatal("No user found for uid %lu", (u_long)getuid());
1263 pw = pwcopy(pw);
1264
1265 logit("session opened for local user %s from [%s]",
1266 pw->pw_name, client_addr);
1267
1268 handle_init();
f546c780 1269
b5e300c2 1270 in = dup(STDIN_FILENO);
1271 out = dup(STDOUT_FILENO);
1272
fe56c12b 1273#ifdef HAVE_CYGWIN
1274 setmode(in, O_BINARY);
1275 setmode(out, O_BINARY);
1276#endif
1277
b5e300c2 1278 max = 0;
1279 if (in > max)
1280 max = in;
1281 if (out > max)
1282 max = out;
1283
1284 buffer_init(&iqueue);
1285 buffer_init(&oqueue);
1286
c8d75031 1287 set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
1288 rset = (fd_set *)xmalloc(set_size);
1289 wset = (fd_set *)xmalloc(set_size);
1290
b5e300c2 1291 for (;;) {
c8d75031 1292 memset(rset, 0, set_size);
1293 memset(wset, 0, set_size);
b5e300c2 1294
c8d75031 1295 FD_SET(in, rset);
b5e300c2 1296 olen = buffer_len(&oqueue);
1297 if (olen > 0)
c8d75031 1298 FD_SET(out, wset);
b5e300c2 1299
c8d75031 1300 if (select(max+1, rset, wset, NULL, NULL) < 0) {
b5e300c2 1301 if (errno == EINTR)
1302 continue;
a13880bb 1303 error("select: %s", strerror(errno));
1304 cleanup_exit(2);
b5e300c2 1305 }
1306
1307 /* copy stdin to iqueue */
c8d75031 1308 if (FD_ISSET(in, rset)) {
b5e300c2 1309 char buf[4*4096];
1310 len = read(in, buf, sizeof buf);
1311 if (len == 0) {
1312 debug("read eof");
a13880bb 1313 cleanup_exit(0);
b5e300c2 1314 } else if (len < 0) {
a13880bb 1315 error("read: %s", strerror(errno));
1316 cleanup_exit(1);
b5e300c2 1317 } else {
1318 buffer_append(&iqueue, buf, len);
1319 }
1320 }
1321 /* send oqueue to stdout */
c8d75031 1322 if (FD_ISSET(out, wset)) {
b5e300c2 1323 len = write(out, buffer_ptr(&oqueue), olen);
1324 if (len < 0) {
a13880bb 1325 error("write: %s", strerror(errno));
1326 cleanup_exit(1);
b5e300c2 1327 } else {
1328 buffer_consume(&oqueue, len);
1329 }
1330 }
1331 /* process requests from client */
1332 process();
1333 }
1334}
This page took 0.670623 seconds and 5 git commands to generate.