summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--sftp-server.831
-rw-r--r--sftp-server.c317
3 files changed, 295 insertions, 58 deletions
diff --git a/ChangeLog b/ChangeLog
index 2e45ea652..e3e7ce825 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -38,6 +38,9 @@
38 - djm@cvs.openbsd.org 2006/07/06 10:47:05 38 - djm@cvs.openbsd.org 2006/07/06 10:47:05
39 [servconf.c servconf.h session.c sshd_config.5] 39 [servconf.c servconf.h session.c sshd_config.5]
40 support arguments to Subsystem commands; ok markus@ 40 support arguments to Subsystem commands; ok markus@
41 - djm@cvs.openbsd.org 2006/07/06 10:47:57
42 [sftp-server.8 sftp-server.c]
43 add commandline options to enable logging of transactions; ok markus@
41 44
4220060706 4520060706
43 - (dtucker) [configure.ac] Try AIX blibpath test in different order when 46 - (dtucker) [configure.ac] Try AIX blibpath test in different order when
@@ -4771,4 +4774,4 @@
4771 - (djm) Trim deprecated options from INSTALL. Mention UsePAM 4774 - (djm) Trim deprecated options from INSTALL. Mention UsePAM
4772 - (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu 4775 - (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu
4773 4776
4774$Id: ChangeLog,v 1.4369 2006/07/10 10:36:47 djm Exp $ 4777$Id: ChangeLog,v 1.4370 2006/07/10 10:46:55 djm Exp $
diff --git a/sftp-server.8 b/sftp-server.8
index 42f5d437c..da68e45fb 100644
--- a/sftp-server.8
+++ b/sftp-server.8
@@ -1,4 +1,4 @@
1.\" $OpenBSD: sftp-server.8,v 1.10 2003/10/08 08:27:36 jmc Exp $ 1.\" $OpenBSD: sftp-server.8,v 1.11 2006/07/06 10:47:57 djm Exp $
2.\" 2.\"
3.\" Copyright (c) 2000 Markus Friedl. All rights reserved. 3.\" Copyright (c) 2000 Markus Friedl. All rights reserved.
4.\" 4.\"
@@ -30,6 +30,8 @@
30.Nd SFTP server subsystem 30.Nd SFTP server subsystem
31.Sh SYNOPSIS 31.Sh SYNOPSIS
32.Nm sftp-server 32.Nm sftp-server
33.Op Fl f Ar log_facility
34.Op Fl l Ar log_level
33.Sh DESCRIPTION 35.Sh DESCRIPTION
34.Nm 36.Nm
35is a program that speaks the server side of SFTP protocol 37is a program that speaks the server side of SFTP protocol
@@ -40,9 +42,36 @@ is not intended to be called directly, but from
40using the 42using the
41.Cm Subsystem 43.Cm Subsystem
42option. 44option.
45.Pp
46Command-line flags to
47.Nm
48should be specified in the
49.Cm Subsystem
50declaration.
43See 51See
44.Xr sshd_config 5 52.Xr sshd_config 5
45for more information. 53for more information.
54.Pp
55Valid options are:
56.Bl -tag -width Ds
57.It Fl f Ar log_facility
58Specifies the facility code that is used when logging messages from
59.Nm .
60The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2,
61LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7.
62The default is AUTH.
63.It Fl l Ar log_level
64Specifies which messages will be logged by
65.Nm .
66The possible values are:
67QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3.
68INFO and VERBOSE log transactions that
69.Nm
70performs on behalf of the client.
71DEBUG and DEBUG1 are equivalent.
72DEBUG2 and DEBUG3 each specify higher levels of debugging output.
73The default is ERROR.
74.El
46.Sh SEE ALSO 75.Sh SEE ALSO
47.Xr sftp 1 , 76.Xr sftp 1 ,
48.Xr ssh 1 , 77.Xr ssh 1 ,
diff --git a/sftp-server.c b/sftp-server.c
index e842341cb..e882216b1 100644
--- a/sftp-server.c
+++ b/sftp-server.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-server.c,v 1.57 2006/03/30 09:58:16 djm Exp $ */ 1/* $OpenBSD: sftp-server.c,v 1.58 2006/07/06 10:47:57 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000-2004 Markus Friedl. All rights reserved.
4 * 4 *
@@ -26,6 +26,7 @@
26#include "log.h" 26#include "log.h"
27#include "xmalloc.h" 27#include "xmalloc.h"
28#include "misc.h" 28#include "misc.h"
29#include "uidswap.h"
29 30
30#include "sftp.h" 31#include "sftp.h"
31#include "sftp-common.h" 32#include "sftp-common.h"
@@ -34,9 +35,13 @@
34#define get_int64() buffer_get_int64(&iqueue); 35#define get_int64() buffer_get_int64(&iqueue);
35#define get_int() buffer_get_int(&iqueue); 36#define get_int() buffer_get_int(&iqueue);
36#define get_string(lenp) buffer_get_string(&iqueue, lenp); 37#define get_string(lenp) buffer_get_string(&iqueue, lenp);
37#define TRACE debug
38 38
39extern char *__progname; 39/* Our verbosity */
40LogLevel log_level = SYSLOG_LEVEL_ERROR;
41
42/* Our client */
43struct passwd *pw = NULL;
44char *client_addr = NULL;
40 45
41/* input and output queue */ 46/* input and output queue */
42Buffer iqueue; 47Buffer iqueue;
@@ -108,6 +113,33 @@ flags_from_portable(int pflags)
108 return flags; 113 return flags;
109} 114}
110 115
116static const char *
117string_from_portable(int pflags)
118{
119 static char ret[128];
120
121 *ret = '\0';
122
123#define PAPPEND(str) { \
124 if (*ret != '\0') \
125 strlcat(ret, ",", sizeof(ret)); \
126 strlcat(ret, str, sizeof(ret)); \
127 }
128
129 if (pflags & SSH2_FXF_READ)
130 PAPPEND("READ")
131 if (pflags & SSH2_FXF_WRITE)
132 PAPPEND("WRITE")
133 if (pflags & SSH2_FXF_CREAT)
134 PAPPEND("CREATE")
135 if (pflags & SSH2_FXF_TRUNC)
136 PAPPEND("TRUNCATE")
137 if (pflags & SSH2_FXF_EXCL)
138 PAPPEND("EXCL")
139
140 return ret;
141}
142
111static Attrib * 143static Attrib *
112get_attrib(void) 144get_attrib(void)
113{ 145{
@@ -122,6 +154,7 @@ struct Handle {
122 DIR *dirp; 154 DIR *dirp;
123 int fd; 155 int fd;
124 char *name; 156 char *name;
157 u_int64_t bytes_read, bytes_write;
125}; 158};
126 159
127enum { 160enum {
@@ -152,6 +185,7 @@ handle_new(int use, const char *name, int fd, DIR *dirp)
152 handles[i].dirp = dirp; 185 handles[i].dirp = dirp;
153 handles[i].fd = fd; 186 handles[i].fd = fd;
154 handles[i].name = xstrdup(name); 187 handles[i].name = xstrdup(name);
188 handles[i].bytes_read = handles[i].bytes_write = 0;
155 return i; 189 return i;
156 } 190 }
157 } 191 }
@@ -215,6 +249,36 @@ handle_to_fd(int handle)
215 return -1; 249 return -1;
216} 250}
217 251
252static void
253handle_update_read(int handle, ssize_t bytes)
254{
255 if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0)
256 handles[handle].bytes_read += bytes;
257}
258
259static void
260handle_update_write(int handle, ssize_t bytes)
261{
262 if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0)
263 handles[handle].bytes_write += bytes;
264}
265
266static u_int64_t
267handle_bytes_read(int handle)
268{
269 if (handle_is_ok(handle, HANDLE_FILE))
270 return (handles[handle].bytes_read);
271 return 0;
272}
273
274static u_int64_t
275handle_bytes_write(int handle)
276{
277 if (handle_is_ok(handle, HANDLE_FILE))
278 return (handles[handle].bytes_write);
279 return 0;
280}
281
218static int 282static int
219handle_close(int handle) 283handle_close(int handle)
220{ 284{
@@ -234,6 +298,31 @@ handle_close(int handle)
234 return ret; 298 return ret;
235} 299}
236 300
301static void
302handle_log_close(int handle, char *emsg)
303{
304 if (handle_is_ok(handle, HANDLE_FILE)) {
305 logit("%s%sclose \"%s\" bytes read %llu written %llu",
306 emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ",
307 handle_to_name(handle),
308 handle_bytes_read(handle), handle_bytes_write(handle));
309 } else {
310 logit("%s%sclosedir \"%s\"",
311 emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ",
312 handle_to_name(handle));
313 }
314}
315
316static void
317handle_log_exit(void)
318{
319 u_int i;
320
321 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
322 if (handles[i].use != HANDLE_UNUSED)
323 handle_log_close(i, "forced");
324}
325
237static int 326static int
238get_handle(void) 327get_handle(void)
239{ 328{
@@ -260,10 +349,9 @@ send_msg(Buffer *m)
260 buffer_consume(m, mlen); 349 buffer_consume(m, mlen);
261} 350}
262 351
263static void 352static const char *
264send_status(u_int32_t id, u_int32_t status) 353status_to_message(u_int32_t status)
265{ 354{
266 Buffer msg;
267 const char *status_messages[] = { 355 const char *status_messages[] = {
268 "Success", /* SSH_FX_OK */ 356 "Success", /* SSH_FX_OK */
269 "End of file", /* SSH_FX_EOF */ 357 "End of file", /* SSH_FX_EOF */
@@ -276,15 +364,24 @@ send_status(u_int32_t id, u_int32_t status)
276 "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 364 "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */
277 "Unknown error" /* Others */ 365 "Unknown error" /* Others */
278 }; 366 };
367 return (status_messages[MIN(status,SSH2_FX_MAX)]);
368}
279 369
280 TRACE("sent status id %u error %u", id, status); 370static void
371send_status(u_int32_t id, u_int32_t status)
372{
373 Buffer msg;
374
375 debug3("request %u: sent status %u", id, status);
376 if (log_level > SYSLOG_LEVEL_VERBOSE ||
377 (status != SSH2_FX_OK && status != SSH2_FX_EOF))
378 logit("sent status %s", status_to_message(status));
281 buffer_init(&msg); 379 buffer_init(&msg);
282 buffer_put_char(&msg, SSH2_FXP_STATUS); 380 buffer_put_char(&msg, SSH2_FXP_STATUS);
283 buffer_put_int(&msg, id); 381 buffer_put_int(&msg, id);
284 buffer_put_int(&msg, status); 382 buffer_put_int(&msg, status);
285 if (version >= 3) { 383 if (version >= 3) {
286 buffer_put_cstring(&msg, 384 buffer_put_cstring(&msg, status_to_message(status));
287 status_messages[MIN(status,SSH2_FX_MAX)]);
288 buffer_put_cstring(&msg, ""); 385 buffer_put_cstring(&msg, "");
289 } 386 }
290 send_msg(&msg); 387 send_msg(&msg);
@@ -306,7 +403,7 @@ send_data_or_handle(char type, u_int32_t id, const char *data, int dlen)
306static void 403static void
307send_data(u_int32_t id, const char *data, int dlen) 404send_data(u_int32_t id, const char *data, int dlen)
308{ 405{
309 TRACE("sent data id %u len %d", id, dlen); 406 debug("request %u: sent data len %d", id, dlen);
310 send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 407 send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
311} 408}
312 409
@@ -317,7 +414,7 @@ send_handle(u_int32_t id, int handle)
317 int hlen; 414 int hlen;
318 415
319 handle_to_string(handle, &string, &hlen); 416 handle_to_string(handle, &string, &hlen);
320 TRACE("sent handle id %u handle %d", id, handle); 417 debug("request %u: sent handle handle %d", id, handle);
321 send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 418 send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
322 xfree(string); 419 xfree(string);
323} 420}
@@ -332,7 +429,7 @@ send_names(u_int32_t id, int count, const Stat *stats)
332 buffer_put_char(&msg, SSH2_FXP_NAME); 429 buffer_put_char(&msg, SSH2_FXP_NAME);
333 buffer_put_int(&msg, id); 430 buffer_put_int(&msg, id);
334 buffer_put_int(&msg, count); 431 buffer_put_int(&msg, count);
335 TRACE("sent names id %u count %d", id, count); 432 debug("request %u: sent names count %d", id, count);
336 for (i = 0; i < count; i++) { 433 for (i = 0; i < count; i++) {
337 buffer_put_cstring(&msg, stats[i].name); 434 buffer_put_cstring(&msg, stats[i].name);
338 buffer_put_cstring(&msg, stats[i].long_name); 435 buffer_put_cstring(&msg, stats[i].long_name);
@@ -347,7 +444,7 @@ send_attrib(u_int32_t id, const Attrib *a)
347{ 444{
348 Buffer msg; 445 Buffer msg;
349 446
350 TRACE("sent attrib id %u have 0x%x", id, a->flags); 447 debug("request %u: sent attrib have 0x%x", id, a->flags);
351 buffer_init(&msg); 448 buffer_init(&msg);
352 buffer_put_char(&msg, SSH2_FXP_ATTRS); 449 buffer_put_char(&msg, SSH2_FXP_ATTRS);
353 buffer_put_int(&msg, id); 450 buffer_put_int(&msg, id);
@@ -364,7 +461,7 @@ process_init(void)
364 Buffer msg; 461 Buffer msg;
365 462
366 version = get_int(); 463 version = get_int();
367 TRACE("client version %d", version); 464 verbose("received client version %d", version);
368 buffer_init(&msg); 465 buffer_init(&msg);
369 buffer_put_char(&msg, SSH2_FXP_VERSION); 466 buffer_put_char(&msg, SSH2_FXP_VERSION);
370 buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 467 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
@@ -381,12 +478,14 @@ process_open(void)
381 int handle, fd, flags, mode, status = SSH2_FX_FAILURE; 478 int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
382 479
383 id = get_int(); 480 id = get_int();
481 debug3("request %u: open flags %d", id, pflags);
384 name = get_string(NULL); 482 name = get_string(NULL);
385 pflags = get_int(); /* portable flags */ 483 pflags = get_int(); /* portable flags */
386 a = get_attrib(); 484 a = get_attrib();
387 flags = flags_from_portable(pflags); 485 flags = flags_from_portable(pflags);
388 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; 486 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
389 TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode); 487 logit("open \"%s\" flags %s mode 0%o",
488 name, string_from_portable(pflags), mode);
390 fd = open(name, flags, mode); 489 fd = open(name, flags, mode);
391 if (fd < 0) { 490 if (fd < 0) {
392 status = errno_to_portable(errno); 491 status = errno_to_portable(errno);
@@ -412,7 +511,8 @@ process_close(void)
412 511
413 id = get_int(); 512 id = get_int();
414 handle = get_handle(); 513 handle = get_handle();
415 TRACE("close id %u handle %d", id, handle); 514 debug3("request %u: close handle %u", id, handle);
515 handle_log_close(handle, NULL);
416 ret = handle_close(handle); 516 ret = handle_close(handle);
417 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 517 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
418 send_status(id, status); 518 send_status(id, status);
@@ -431,11 +531,11 @@ process_read(void)
431 off = get_int64(); 531 off = get_int64();
432 len = get_int(); 532 len = get_int();
433 533
434 TRACE("read id %u handle %d off %llu len %d", id, handle, 534 debug("request %u: read \"%s\" (handle %d) off %llu len %d",
435 (unsigned long long)off, len); 535 id, handle_to_name(handle), handle, (unsigned long long)off, len);
436 if (len > sizeof buf) { 536 if (len > sizeof buf) {
437 len = sizeof buf; 537 len = sizeof buf;
438 logit("read change len %d", len); 538 debug2("read change len %d", len);
439 } 539 }
440 fd = handle_to_fd(handle); 540 fd = handle_to_fd(handle);
441 if (fd >= 0) { 541 if (fd >= 0) {
@@ -451,6 +551,7 @@ process_read(void)
451 } else { 551 } else {
452 send_data(id, buf, ret); 552 send_data(id, buf, ret);
453 status = SSH2_FX_OK; 553 status = SSH2_FX_OK;
554 handle_update_read(handle, ret);
454 } 555 }
455 } 556 }
456 } 557 }
@@ -472,8 +573,8 @@ process_write(void)
472 off = get_int64(); 573 off = get_int64();
473 data = get_string(&len); 574 data = get_string(&len);
474 575
475 TRACE("write id %u handle %d off %llu len %d", id, handle, 576 debug("request %u: write \"%s\" (handle %d) off %llu len %d",
476 (unsigned long long)off, len); 577 id, handle_to_name(handle), handle, (unsigned long long)off, len);
477 fd = handle_to_fd(handle); 578 fd = handle_to_fd(handle);
478 if (fd >= 0) { 579 if (fd >= 0) {
479 if (lseek(fd, off, SEEK_SET) < 0) { 580 if (lseek(fd, off, SEEK_SET) < 0) {
@@ -487,8 +588,9 @@ process_write(void)
487 status = errno_to_portable(errno); 588 status = errno_to_portable(errno);
488 } else if ((size_t)ret == len) { 589 } else if ((size_t)ret == len) {
489 status = SSH2_FX_OK; 590 status = SSH2_FX_OK;
591 handle_update_write(handle, ret);
490 } else { 592 } else {
491 logit("nothing at all written"); 593 debug2("nothing at all written");
492 } 594 }
493 } 595 }
494 } 596 }
@@ -507,7 +609,8 @@ process_do_stat(int do_lstat)
507 609
508 id = get_int(); 610 id = get_int();
509 name = get_string(NULL); 611 name = get_string(NULL);
510 TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name); 612 debug3("request %u: %sstat", id, do_lstat ? "l" : "");
613 verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name);
511 ret = do_lstat ? lstat(name, &st) : stat(name, &st); 614 ret = do_lstat ? lstat(name, &st) : stat(name, &st);
512 if (ret < 0) { 615 if (ret < 0) {
513 status = errno_to_portable(errno); 616 status = errno_to_portable(errno);
@@ -543,7 +646,8 @@ process_fstat(void)
543 646
544 id = get_int(); 647 id = get_int();
545 handle = get_handle(); 648 handle = get_handle();
546 TRACE("fstat id %u handle %d", id, handle); 649 debug("request %u: fstat \"%s\" (handle %u)",
650 id, handle_to_name(handle), handle);
547 fd = handle_to_fd(handle); 651 fd = handle_to_fd(handle);
548 if (fd >= 0) { 652 if (fd >= 0) {
549 ret = fstat(fd, &st); 653 ret = fstat(fd, &st);
@@ -582,23 +686,33 @@ process_setstat(void)
582 id = get_int(); 686 id = get_int();
583 name = get_string(NULL); 687 name = get_string(NULL);
584 a = get_attrib(); 688 a = get_attrib();
585 TRACE("setstat id %u name %s", id, name); 689 debug("request %u: setstat name \"%s\"", id, name);
586 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 690 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
691 logit("set \"%s\" size %llu", name, a->size);
587 ret = truncate(name, a->size); 692 ret = truncate(name, a->size);
588 if (ret == -1) 693 if (ret == -1)
589 status = errno_to_portable(errno); 694 status = errno_to_portable(errno);
590 } 695 }
591 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 696 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
697 logit("set \"%s\" mode %04o", name, a->perm);
592 ret = chmod(name, a->perm & 0777); 698 ret = chmod(name, a->perm & 0777);
593 if (ret == -1) 699 if (ret == -1)
594 status = errno_to_portable(errno); 700 status = errno_to_portable(errno);
595 } 701 }
596 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 702 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
703 char buf[64];
704 time_t t = a->mtime;
705
706 strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S",
707 localtime(&t));
708 logit("set \"%s\" modtime %s", name, buf);
597 ret = utimes(name, attrib_to_tv(a)); 709 ret = utimes(name, attrib_to_tv(a));
598 if (ret == -1) 710 if (ret == -1)
599 status = errno_to_portable(errno); 711 status = errno_to_portable(errno);
600 } 712 }
601 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 713 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
714 logit("set \"%s\" owner %lu group %lu", name,
715 (u_long)a->uid, (u_long)a->gid);
602 ret = chown(name, a->uid, a->gid); 716 ret = chown(name, a->uid, a->gid);
603 if (ret == -1) 717 if (ret == -1)
604 status = errno_to_portable(errno); 718 status = errno_to_portable(errno);
@@ -614,23 +728,25 @@ process_fsetstat(void)
614 u_int32_t id; 728 u_int32_t id;
615 int handle, fd, ret; 729 int handle, fd, ret;
616 int status = SSH2_FX_OK; 730 int status = SSH2_FX_OK;
617 char *name;
618 731
619 id = get_int(); 732 id = get_int();
620 handle = get_handle(); 733 handle = get_handle();
621 a = get_attrib(); 734 a = get_attrib();
622 TRACE("fsetstat id %u handle %d", id, handle); 735 debug("request %u: fsetstat handle %d", id, handle);
623 fd = handle_to_fd(handle); 736 fd = handle_to_fd(handle);
624 name = handle_to_name(handle); 737 if (fd < 0) {
625 if (fd < 0 || name == NULL) {
626 status = SSH2_FX_FAILURE; 738 status = SSH2_FX_FAILURE;
627 } else { 739 } else {
740 char *name = handle_to_name(handle);
741
628 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 742 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
743 logit("set \"%s\" size %llu", name, a->size);
629 ret = ftruncate(fd, a->size); 744 ret = ftruncate(fd, a->size);
630 if (ret == -1) 745 if (ret == -1)
631 status = errno_to_portable(errno); 746 status = errno_to_portable(errno);
632 } 747 }
633 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 748 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
749 logit("set \"%s\" mode %04o", name, a->perm);
634#ifdef HAVE_FCHMOD 750#ifdef HAVE_FCHMOD
635 ret = fchmod(fd, a->perm & 0777); 751 ret = fchmod(fd, a->perm & 0777);
636#else 752#else
@@ -640,6 +756,12 @@ process_fsetstat(void)
640 status = errno_to_portable(errno); 756 status = errno_to_portable(errno);
641 } 757 }
642 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 758 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
759 char buf[64];
760 time_t t = a->mtime;
761
762 strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S",
763 localtime(&t));
764 logit("set \"%s\" modtime %s", name, buf);
643#ifdef HAVE_FUTIMES 765#ifdef HAVE_FUTIMES
644 ret = futimes(fd, attrib_to_tv(a)); 766 ret = futimes(fd, attrib_to_tv(a));
645#else 767#else
@@ -649,6 +771,8 @@ process_fsetstat(void)
649 status = errno_to_portable(errno); 771 status = errno_to_portable(errno);
650 } 772 }
651 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 773 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
774 logit("set \"%s\" owner %lu group %lu", name,
775 (u_long)a->uid, (u_long)a->gid);
652#ifdef HAVE_FCHOWN 776#ifdef HAVE_FCHOWN
653 ret = fchown(fd, a->uid, a->gid); 777 ret = fchown(fd, a->uid, a->gid);
654#else 778#else
@@ -671,7 +795,8 @@ process_opendir(void)
671 795
672 id = get_int(); 796 id = get_int();
673 path = get_string(NULL); 797 path = get_string(NULL);
674 TRACE("opendir id %u path %s", id, path); 798 debug3("request %u: opendir", id);
799 logit("opendir \"%s\"", path);
675 dirp = opendir(path); 800 dirp = opendir(path);
676 if (dirp == NULL) { 801 if (dirp == NULL) {
677 status = errno_to_portable(errno); 802 status = errno_to_portable(errno);
@@ -701,14 +826,15 @@ process_readdir(void)
701 826
702 id = get_int(); 827 id = get_int();
703 handle = get_handle(); 828 handle = get_handle();
704 TRACE("readdir id %u handle %d", id, handle); 829 debug("request %u: readdir \"%s\" (handle %d)", id,
830 handle_to_name(handle), handle);
705 dirp = handle_to_dir(handle); 831 dirp = handle_to_dir(handle);
706 path = handle_to_name(handle); 832 path = handle_to_name(handle);
707 if (dirp == NULL || path == NULL) { 833 if (dirp == NULL || path == NULL) {
708 send_status(id, SSH2_FX_FAILURE); 834 send_status(id, SSH2_FX_FAILURE);
709 } else { 835 } else {
710 struct stat st; 836 struct stat st;
711 char pathname[1024]; 837 char pathname[MAXPATHLEN];
712 Stat *stats; 838 Stat *stats;
713 int nstats = 10, count = 0, i; 839 int nstats = 10, count = 0, i;
714 840
@@ -755,7 +881,8 @@ process_remove(void)
755 881
756 id = get_int(); 882 id = get_int();
757 name = get_string(NULL); 883 name = get_string(NULL);
758 TRACE("remove id %u name %s", id, name); 884 debug3("request %u: remove", id);
885 logit("remove name \"%s\"", name);
759 ret = unlink(name); 886 ret = unlink(name);
760 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 887 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
761 send_status(id, status); 888 send_status(id, status);
@@ -775,7 +902,8 @@ process_mkdir(void)
775 a = get_attrib(); 902 a = get_attrib();
776 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 903 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
777 a->perm & 0777 : 0777; 904 a->perm & 0777 : 0777;
778 TRACE("mkdir id %u name %s mode 0%o", id, name, mode); 905 debug3("request %u: mkdir", id);
906 logit("mkdir name \"%s\" mode 0%o", name, mode);
779 ret = mkdir(name, mode); 907 ret = mkdir(name, mode);
780 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 908 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
781 send_status(id, status); 909 send_status(id, status);
@@ -791,7 +919,8 @@ process_rmdir(void)
791 919
792 id = get_int(); 920 id = get_int();
793 name = get_string(NULL); 921 name = get_string(NULL);
794 TRACE("rmdir id %u name %s", id, name); 922 debug3("request %u: rmdir", id);
923 logit("rmdir name \"%s\"", name);
795 ret = rmdir(name); 924 ret = rmdir(name);
796 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 925 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
797 send_status(id, status); 926 send_status(id, status);
@@ -811,7 +940,8 @@ process_realpath(void)
811 xfree(path); 940 xfree(path);
812 path = xstrdup("."); 941 path = xstrdup(".");
813 } 942 }
814 TRACE("realpath id %u path %s", id, path); 943 debug3("request %u: realpath", id);
944 verbose("realpath \"%s\"", path);
815 if (realpath(path, resolvedname) == NULL) { 945 if (realpath(path, resolvedname) == NULL) {
816 send_status(id, errno_to_portable(errno)); 946 send_status(id, errno_to_portable(errno));
817 } else { 947 } else {
@@ -834,7 +964,8 @@ process_rename(void)
834 id = get_int(); 964 id = get_int();
835 oldpath = get_string(NULL); 965 oldpath = get_string(NULL);
836 newpath = get_string(NULL); 966 newpath = get_string(NULL);
837 TRACE("rename id %u old %s new %s", id, oldpath, newpath); 967 debug3("request %u: rename", id);
968 logit("rename old \"%s\" new \"%s\"", oldpath, newpath);
838 status = SSH2_FX_FAILURE; 969 status = SSH2_FX_FAILURE;
839 if (lstat(oldpath, &sb) == -1) 970 if (lstat(oldpath, &sb) == -1)
840 status = errno_to_portable(errno); 971 status = errno_to_portable(errno);
@@ -889,7 +1020,8 @@ process_readlink(void)
889 1020
890 id = get_int(); 1021 id = get_int();
891 path = get_string(NULL); 1022 path = get_string(NULL);
892 TRACE("readlink id %u path %s", id, path); 1023 debug3("request %u: readlink", id);
1024 verbose("readlink \"%s\"", path);
893 if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) 1025 if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1)
894 send_status(id, errno_to_portable(errno)); 1026 send_status(id, errno_to_portable(errno));
895 else { 1027 else {
@@ -913,7 +1045,8 @@ process_symlink(void)
913 id = get_int(); 1045 id = get_int();
914 oldpath = get_string(NULL); 1046 oldpath = get_string(NULL);
915 newpath = get_string(NULL); 1047 newpath = get_string(NULL);
916 TRACE("symlink id %u old %s new %s", id, oldpath, newpath); 1048 debug3("request %u: symlink", id);
1049 logit("symlink old \"%s\" new \"%s\"", oldpath, newpath);
917 /* this will fail if 'newpath' exists */ 1050 /* this will fail if 'newpath' exists */
918 ret = symlink(oldpath, newpath); 1051 ret = symlink(oldpath, newpath);
919 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 1052 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
@@ -951,8 +1084,9 @@ process(void)
951 cp = buffer_ptr(&iqueue); 1084 cp = buffer_ptr(&iqueue);
952 msg_len = get_u32(cp); 1085 msg_len = get_u32(cp);
953 if (msg_len > SFTP_MAX_MSG_LENGTH) { 1086 if (msg_len > SFTP_MAX_MSG_LENGTH) {
954 error("bad message "); 1087 error("bad message from %s local user %s",
955 exit(11); 1088 client_addr, pw->pw_name);
1089 cleanup_exit(11);
956 } 1090 }
957 if (buf_len < msg_len + 4) 1091 if (buf_len < msg_len + 4)
958 return; 1092 return;
@@ -1026,7 +1160,7 @@ process(void)
1026 } 1160 }
1027 /* discard the remaining bytes from the current packet */ 1161 /* discard the remaining bytes from the current packet */
1028 if (buf_len < buffer_len(&iqueue)) 1162 if (buf_len < buffer_len(&iqueue))
1029 fatal("iqueue grows"); 1163 fatal("iqueue grew unexpectedly");
1030 consumed = buf_len - buffer_len(&iqueue); 1164 consumed = buf_len - buffer_len(&iqueue);
1031 if (msg_len < consumed) 1165 if (msg_len < consumed)
1032 fatal("msg_len %d < consumed %d", msg_len, consumed); 1166 fatal("msg_len %d < consumed %d", msg_len, consumed);
@@ -1034,24 +1168,94 @@ process(void)
1034 buffer_consume(&iqueue, msg_len - consumed); 1168 buffer_consume(&iqueue, msg_len - consumed);
1035} 1169}
1036 1170
1171/* Cleanup handler that logs active handles upon normal exit */
1172void
1173cleanup_exit(int i)
1174{
1175 if (pw != NULL && client_addr != NULL) {
1176 handle_log_exit();
1177 logit("session closed for local user %s from [%s]",
1178 pw->pw_name, client_addr);
1179 }
1180 _exit(i);
1181}
1182
1183static void
1184usage(void)
1185{
1186 extern char *__progname;
1187
1188 fprintf(stderr,
1189 "usage: %s [-he] [-l log_level] [-f log_facility]\n", __progname);
1190 exit(1);
1191}
1192
1037int 1193int
1038main(int ac, char **av) 1194main(int argc, char **argv)
1039{ 1195{
1040 fd_set *rset, *wset; 1196 fd_set *rset, *wset;
1041 int in, out, max; 1197 int in, out, max, ch, skipargs = 0, log_stderr = 0;
1042 ssize_t len, olen, set_size; 1198 ssize_t len, olen, set_size;
1199 SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
1200 char *cp;
1201
1202 extern int optind;
1203 extern char *optarg;
1204 extern char *__progname;
1043 1205
1044 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 1206 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1045 sanitise_stdfd(); 1207 sanitise_stdfd();
1046 1208
1047 /* XXX should use getopt */ 1209 __progname = ssh_get_progname(argv[0]);
1210 log_init(__progname, log_level, log_facility, log_stderr);
1211
1212 while (!skipargs && (ch = getopt(argc, argv, "C:f:l:che")) != -1) {
1213 switch (ch) {
1214 case 'c':
1215 /*
1216 * Ignore all arguments if we are invoked as a
1217 * shell using "sftp-server -c command"
1218 */
1219 skipargs = 1;
1220 break;
1221 case 'e':
1222 log_stderr = 1;
1223 break;
1224 case 'l':
1225 log_level = log_level_number(optarg);
1226 if (log_level == SYSLOG_LEVEL_NOT_SET)
1227 error("Invalid log level \"%s\"", optarg);
1228 break;
1229 case 'f':
1230 log_facility = log_facility_number(optarg);
1231 if (log_level == SYSLOG_FACILITY_NOT_SET)
1232 error("Invalid log facility \"%s\"", optarg);
1233 break;
1234 case 'h':
1235 default:
1236 usage();
1237 }
1238 }
1048 1239
1049 __progname = ssh_get_progname(av[0]); 1240 log_init(__progname, log_level, log_facility, log_stderr);
1050 handle_init();
1051 1241
1052#ifdef DEBUG_SFTP_SERVER 1242 if ((cp = getenv("SSH_CONNECTION")) != NULL) {
1053 log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); 1243 client_addr = xstrdup(cp);
1054#endif 1244 if ((cp = strchr(client_addr, ' ')) == NULL)
1245 fatal("Malformed SSH_CONNECTION variable: \"%s\"",
1246 getenv("SSH_CONNECTION"));
1247 *cp = '\0';
1248 } else
1249 client_addr = xstrdup("UNKNOWN");
1250
1251 if ((pw = getpwuid(getuid())) == NULL)
1252 fatal("No user found for uid %lu", (u_long)getuid());
1253 pw = pwcopy(pw);
1254
1255 logit("session opened for local user %s from [%s]",
1256 pw->pw_name, client_addr);
1257
1258 handle_init();
1055 1259
1056 in = dup(STDIN_FILENO); 1260 in = dup(STDIN_FILENO);
1057 out = dup(STDOUT_FILENO); 1261 out = dup(STDOUT_FILENO);
@@ -1086,7 +1290,8 @@ main(int ac, char **av)
1086 if (select(max+1, rset, wset, NULL, NULL) < 0) { 1290 if (select(max+1, rset, wset, NULL, NULL) < 0) {
1087 if (errno == EINTR) 1291 if (errno == EINTR)
1088 continue; 1292 continue;
1089 exit(2); 1293 error("select: %s", strerror(errno));
1294 cleanup_exit(2);
1090 } 1295 }
1091 1296
1092 /* copy stdin to iqueue */ 1297 /* copy stdin to iqueue */
@@ -1095,10 +1300,10 @@ main(int ac, char **av)
1095 len = read(in, buf, sizeof buf); 1300 len = read(in, buf, sizeof buf);
1096 if (len == 0) { 1301 if (len == 0) {
1097 debug("read eof"); 1302 debug("read eof");
1098 exit(0); 1303 cleanup_exit(0);
1099 } else if (len < 0) { 1304 } else if (len < 0) {
1100 error("read error"); 1305 error("read: %s", strerror(errno));
1101 exit(1); 1306 cleanup_exit(1);
1102 } else { 1307 } else {
1103 buffer_append(&iqueue, buf, len); 1308 buffer_append(&iqueue, buf, len);
1104 } 1309 }
@@ -1107,8 +1312,8 @@ main(int ac, char **av)
1107 if (FD_ISSET(out, wset)) { 1312 if (FD_ISSET(out, wset)) {
1108 len = write(out, buffer_ptr(&oqueue), olen); 1313 len = write(out, buffer_ptr(&oqueue), olen);
1109 if (len < 0) { 1314 if (len < 0) {
1110 error("write error"); 1315 error("write: %s", strerror(errno));
1111 exit(1); 1316 cleanup_exit(1);
1112 } else { 1317 } else {
1113 buffer_consume(&oqueue, len); 1318 buffer_consume(&oqueue, len);
1114 } 1319 }