diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | sftp-server.8 | 31 | ||||
-rw-r--r-- | sftp-server.c | 317 |
3 files changed, 295 insertions, 58 deletions
@@ -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 | ||
42 | 20060706 | 45 | 20060706 |
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 |
35 | is a program that speaks the server side of SFTP protocol | 37 | is a program that speaks the server side of SFTP protocol |
@@ -40,9 +42,36 @@ is not intended to be called directly, but from | |||
40 | using the | 42 | using the |
41 | .Cm Subsystem | 43 | .Cm Subsystem |
42 | option. | 44 | option. |
45 | .Pp | ||
46 | Command-line flags to | ||
47 | .Nm | ||
48 | should be specified in the | ||
49 | .Cm Subsystem | ||
50 | declaration. | ||
43 | See | 51 | See |
44 | .Xr sshd_config 5 | 52 | .Xr sshd_config 5 |
45 | for more information. | 53 | for more information. |
54 | .Pp | ||
55 | Valid options are: | ||
56 | .Bl -tag -width Ds | ||
57 | .It Fl f Ar log_facility | ||
58 | Specifies the facility code that is used when logging messages from | ||
59 | .Nm . | ||
60 | The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, | ||
61 | LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. | ||
62 | The default is AUTH. | ||
63 | .It Fl l Ar log_level | ||
64 | Specifies which messages will be logged by | ||
65 | .Nm . | ||
66 | The possible values are: | ||
67 | QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. | ||
68 | INFO and VERBOSE log transactions that | ||
69 | .Nm | ||
70 | performs on behalf of the client. | ||
71 | DEBUG and DEBUG1 are equivalent. | ||
72 | DEBUG2 and DEBUG3 each specify higher levels of debugging output. | ||
73 | The 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 | ||
39 | extern char *__progname; | 39 | /* Our verbosity */ |
40 | LogLevel log_level = SYSLOG_LEVEL_ERROR; | ||
41 | |||
42 | /* Our client */ | ||
43 | struct passwd *pw = NULL; | ||
44 | char *client_addr = NULL; | ||
40 | 45 | ||
41 | /* input and output queue */ | 46 | /* input and output queue */ |
42 | Buffer iqueue; | 47 | Buffer iqueue; |
@@ -108,6 +113,33 @@ flags_from_portable(int pflags) | |||
108 | return flags; | 113 | return flags; |
109 | } | 114 | } |
110 | 115 | ||
116 | static const char * | ||
117 | string_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 | |||
111 | static Attrib * | 143 | static Attrib * |
112 | get_attrib(void) | 144 | get_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 | ||
127 | enum { | 160 | enum { |
@@ -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 | ||
252 | static void | ||
253 | handle_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 | |||
259 | static void | ||
260 | handle_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 | |||
266 | static u_int64_t | ||
267 | handle_bytes_read(int handle) | ||
268 | { | ||
269 | if (handle_is_ok(handle, HANDLE_FILE)) | ||
270 | return (handles[handle].bytes_read); | ||
271 | return 0; | ||
272 | } | ||
273 | |||
274 | static u_int64_t | ||
275 | handle_bytes_write(int handle) | ||
276 | { | ||
277 | if (handle_is_ok(handle, HANDLE_FILE)) | ||
278 | return (handles[handle].bytes_write); | ||
279 | return 0; | ||
280 | } | ||
281 | |||
218 | static int | 282 | static int |
219 | handle_close(int handle) | 283 | handle_close(int handle) |
220 | { | 284 | { |
@@ -234,6 +298,31 @@ handle_close(int handle) | |||
234 | return ret; | 298 | return ret; |
235 | } | 299 | } |
236 | 300 | ||
301 | static void | ||
302 | handle_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 | |||
316 | static void | ||
317 | handle_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 | |||
237 | static int | 326 | static int |
238 | get_handle(void) | 327 | get_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 | ||
263 | static void | 352 | static const char * |
264 | send_status(u_int32_t id, u_int32_t status) | 353 | status_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); | 370 | static void |
371 | send_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) | |||
306 | static void | 403 | static void |
307 | send_data(u_int32_t id, const char *data, int dlen) | 404 | send_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 */ | ||
1172 | void | ||
1173 | cleanup_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 | |||
1183 | static void | ||
1184 | usage(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 | |||
1037 | int | 1193 | int |
1038 | main(int ac, char **av) | 1194 | main(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 | } |