diff options
Diffstat (limited to 'sftp-server.c')
-rw-r--r-- | sftp-server.c | 352 |
1 files changed, 287 insertions, 65 deletions
diff --git a/sftp-server.c b/sftp-server.c index 7060c44ad..64777beff 100644 --- a/sftp-server.c +++ b/sftp-server.c | |||
@@ -1,3 +1,4 @@ | |||
1 | /* $OpenBSD: sftp-server.c,v 1.71 2007/01/03 07:22:36 stevesk Exp $ */ | ||
1 | /* | 2 | /* |
2 | * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. | 3 | * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. |
3 | * | 4 | * |
@@ -13,15 +14,33 @@ | |||
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | */ | 16 | */ |
17 | |||
16 | #include "includes.h" | 18 | #include "includes.h" |
17 | RCSID("$OpenBSD: sftp-server.c,v 1.50 2006/01/02 01:20:31 djm Exp $"); | ||
18 | 19 | ||
20 | #include <sys/types.h> | ||
21 | #include <sys/param.h> | ||
22 | #include <sys/stat.h> | ||
23 | #ifdef HAVE_SYS_TIME_H | ||
24 | # include <sys/time.h> | ||
25 | #endif | ||
26 | |||
27 | #include <dirent.h> | ||
28 | #include <errno.h> | ||
29 | #include <fcntl.h> | ||
30 | #include <pwd.h> | ||
31 | #include <stdlib.h> | ||
32 | #include <stdio.h> | ||
33 | #include <string.h> | ||
34 | #include <pwd.h> | ||
35 | #include <time.h> | ||
36 | #include <unistd.h> | ||
37 | #include <stdarg.h> | ||
38 | |||
39 | #include "xmalloc.h" | ||
19 | #include "buffer.h" | 40 | #include "buffer.h" |
20 | #include "bufaux.h" | ||
21 | #include "getput.h" | ||
22 | #include "log.h" | 41 | #include "log.h" |
23 | #include "xmalloc.h" | ||
24 | #include "misc.h" | 42 | #include "misc.h" |
43 | #include "uidswap.h" | ||
25 | 44 | ||
26 | #include "sftp.h" | 45 | #include "sftp.h" |
27 | #include "sftp-common.h" | 46 | #include "sftp-common.h" |
@@ -30,9 +49,13 @@ RCSID("$OpenBSD: sftp-server.c,v 1.50 2006/01/02 01:20:31 djm Exp $"); | |||
30 | #define get_int64() buffer_get_int64(&iqueue); | 49 | #define get_int64() buffer_get_int64(&iqueue); |
31 | #define get_int() buffer_get_int(&iqueue); | 50 | #define get_int() buffer_get_int(&iqueue); |
32 | #define get_string(lenp) buffer_get_string(&iqueue, lenp); | 51 | #define get_string(lenp) buffer_get_string(&iqueue, lenp); |
33 | #define TRACE debug | ||
34 | 52 | ||
35 | extern char *__progname; | 53 | /* Our verbosity */ |
54 | LogLevel log_level = SYSLOG_LEVEL_ERROR; | ||
55 | |||
56 | /* Our client */ | ||
57 | struct passwd *pw = NULL; | ||
58 | char *client_addr = NULL; | ||
36 | 59 | ||
37 | /* input and output queue */ | 60 | /* input and output queue */ |
38 | Buffer iqueue; | 61 | Buffer iqueue; |
@@ -104,6 +127,33 @@ flags_from_portable(int pflags) | |||
104 | return flags; | 127 | return flags; |
105 | } | 128 | } |
106 | 129 | ||
130 | static const char * | ||
131 | string_from_portable(int pflags) | ||
132 | { | ||
133 | static char ret[128]; | ||
134 | |||
135 | *ret = '\0'; | ||
136 | |||
137 | #define PAPPEND(str) { \ | ||
138 | if (*ret != '\0') \ | ||
139 | strlcat(ret, ",", sizeof(ret)); \ | ||
140 | strlcat(ret, str, sizeof(ret)); \ | ||
141 | } | ||
142 | |||
143 | if (pflags & SSH2_FXF_READ) | ||
144 | PAPPEND("READ") | ||
145 | if (pflags & SSH2_FXF_WRITE) | ||
146 | PAPPEND("WRITE") | ||
147 | if (pflags & SSH2_FXF_CREAT) | ||
148 | PAPPEND("CREATE") | ||
149 | if (pflags & SSH2_FXF_TRUNC) | ||
150 | PAPPEND("TRUNCATE") | ||
151 | if (pflags & SSH2_FXF_EXCL) | ||
152 | PAPPEND("EXCL") | ||
153 | |||
154 | return ret; | ||
155 | } | ||
156 | |||
107 | static Attrib * | 157 | static Attrib * |
108 | get_attrib(void) | 158 | get_attrib(void) |
109 | { | 159 | { |
@@ -118,6 +168,7 @@ struct Handle { | |||
118 | DIR *dirp; | 168 | DIR *dirp; |
119 | int fd; | 169 | int fd; |
120 | char *name; | 170 | char *name; |
171 | u_int64_t bytes_read, bytes_write; | ||
121 | }; | 172 | }; |
122 | 173 | ||
123 | enum { | 174 | enum { |
@@ -148,6 +199,7 @@ handle_new(int use, const char *name, int fd, DIR *dirp) | |||
148 | handles[i].dirp = dirp; | 199 | handles[i].dirp = dirp; |
149 | handles[i].fd = fd; | 200 | handles[i].fd = fd; |
150 | handles[i].name = xstrdup(name); | 201 | handles[i].name = xstrdup(name); |
202 | handles[i].bytes_read = handles[i].bytes_write = 0; | ||
151 | return i; | 203 | return i; |
152 | } | 204 | } |
153 | } | 205 | } |
@@ -167,7 +219,7 @@ handle_to_string(int handle, char **stringp, int *hlenp) | |||
167 | if (stringp == NULL || hlenp == NULL) | 219 | if (stringp == NULL || hlenp == NULL) |
168 | return -1; | 220 | return -1; |
169 | *stringp = xmalloc(sizeof(int32_t)); | 221 | *stringp = xmalloc(sizeof(int32_t)); |
170 | PUT_32BIT(*stringp, handle); | 222 | put_u32(*stringp, handle); |
171 | *hlenp = sizeof(int32_t); | 223 | *hlenp = sizeof(int32_t); |
172 | return 0; | 224 | return 0; |
173 | } | 225 | } |
@@ -179,7 +231,7 @@ handle_from_string(const char *handle, u_int hlen) | |||
179 | 231 | ||
180 | if (hlen != sizeof(int32_t)) | 232 | if (hlen != sizeof(int32_t)) |
181 | return -1; | 233 | return -1; |
182 | val = GET_32BIT(handle); | 234 | val = get_u32(handle); |
183 | if (handle_is_ok(val, HANDLE_FILE) || | 235 | if (handle_is_ok(val, HANDLE_FILE) || |
184 | handle_is_ok(val, HANDLE_DIR)) | 236 | handle_is_ok(val, HANDLE_DIR)) |
185 | return val; | 237 | return val; |
@@ -211,6 +263,36 @@ handle_to_fd(int handle) | |||
211 | return -1; | 263 | return -1; |
212 | } | 264 | } |
213 | 265 | ||
266 | static void | ||
267 | handle_update_read(int handle, ssize_t bytes) | ||
268 | { | ||
269 | if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) | ||
270 | handles[handle].bytes_read += bytes; | ||
271 | } | ||
272 | |||
273 | static void | ||
274 | handle_update_write(int handle, ssize_t bytes) | ||
275 | { | ||
276 | if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) | ||
277 | handles[handle].bytes_write += bytes; | ||
278 | } | ||
279 | |||
280 | static u_int64_t | ||
281 | handle_bytes_read(int handle) | ||
282 | { | ||
283 | if (handle_is_ok(handle, HANDLE_FILE)) | ||
284 | return (handles[handle].bytes_read); | ||
285 | return 0; | ||
286 | } | ||
287 | |||
288 | static u_int64_t | ||
289 | handle_bytes_write(int handle) | ||
290 | { | ||
291 | if (handle_is_ok(handle, HANDLE_FILE)) | ||
292 | return (handles[handle].bytes_write); | ||
293 | return 0; | ||
294 | } | ||
295 | |||
214 | static int | 296 | static int |
215 | handle_close(int handle) | 297 | handle_close(int handle) |
216 | { | 298 | { |
@@ -230,6 +312,31 @@ handle_close(int handle) | |||
230 | return ret; | 312 | return ret; |
231 | } | 313 | } |
232 | 314 | ||
315 | static void | ||
316 | handle_log_close(int handle, char *emsg) | ||
317 | { | ||
318 | if (handle_is_ok(handle, HANDLE_FILE)) { | ||
319 | logit("%s%sclose \"%s\" bytes read %llu written %llu", | ||
320 | emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", | ||
321 | handle_to_name(handle), | ||
322 | handle_bytes_read(handle), handle_bytes_write(handle)); | ||
323 | } else { | ||
324 | logit("%s%sclosedir \"%s\"", | ||
325 | emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", | ||
326 | handle_to_name(handle)); | ||
327 | } | ||
328 | } | ||
329 | |||
330 | static void | ||
331 | handle_log_exit(void) | ||
332 | { | ||
333 | u_int i; | ||
334 | |||
335 | for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) | ||
336 | if (handles[i].use != HANDLE_UNUSED) | ||
337 | handle_log_close(i, "forced"); | ||
338 | } | ||
339 | |||
233 | static int | 340 | static int |
234 | get_handle(void) | 341 | get_handle(void) |
235 | { | 342 | { |
@@ -256,10 +363,9 @@ send_msg(Buffer *m) | |||
256 | buffer_consume(m, mlen); | 363 | buffer_consume(m, mlen); |
257 | } | 364 | } |
258 | 365 | ||
259 | static void | 366 | static const char * |
260 | send_status(u_int32_t id, u_int32_t status) | 367 | status_to_message(u_int32_t status) |
261 | { | 368 | { |
262 | Buffer msg; | ||
263 | const char *status_messages[] = { | 369 | const char *status_messages[] = { |
264 | "Success", /* SSH_FX_OK */ | 370 | "Success", /* SSH_FX_OK */ |
265 | "End of file", /* SSH_FX_EOF */ | 371 | "End of file", /* SSH_FX_EOF */ |
@@ -272,15 +378,24 @@ send_status(u_int32_t id, u_int32_t status) | |||
272 | "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ | 378 | "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ |
273 | "Unknown error" /* Others */ | 379 | "Unknown error" /* Others */ |
274 | }; | 380 | }; |
381 | return (status_messages[MIN(status,SSH2_FX_MAX)]); | ||
382 | } | ||
383 | |||
384 | static void | ||
385 | send_status(u_int32_t id, u_int32_t status) | ||
386 | { | ||
387 | Buffer msg; | ||
275 | 388 | ||
276 | TRACE("sent status id %u error %u", id, status); | 389 | debug3("request %u: sent status %u", id, status); |
390 | if (log_level > SYSLOG_LEVEL_VERBOSE || | ||
391 | (status != SSH2_FX_OK && status != SSH2_FX_EOF)) | ||
392 | logit("sent status %s", status_to_message(status)); | ||
277 | buffer_init(&msg); | 393 | buffer_init(&msg); |
278 | buffer_put_char(&msg, SSH2_FXP_STATUS); | 394 | buffer_put_char(&msg, SSH2_FXP_STATUS); |
279 | buffer_put_int(&msg, id); | 395 | buffer_put_int(&msg, id); |
280 | buffer_put_int(&msg, status); | 396 | buffer_put_int(&msg, status); |
281 | if (version >= 3) { | 397 | if (version >= 3) { |
282 | buffer_put_cstring(&msg, | 398 | buffer_put_cstring(&msg, status_to_message(status)); |
283 | status_messages[MIN(status,SSH2_FX_MAX)]); | ||
284 | buffer_put_cstring(&msg, ""); | 399 | buffer_put_cstring(&msg, ""); |
285 | } | 400 | } |
286 | send_msg(&msg); | 401 | send_msg(&msg); |
@@ -302,7 +417,7 @@ send_data_or_handle(char type, u_int32_t id, const char *data, int dlen) | |||
302 | static void | 417 | static void |
303 | send_data(u_int32_t id, const char *data, int dlen) | 418 | send_data(u_int32_t id, const char *data, int dlen) |
304 | { | 419 | { |
305 | TRACE("sent data id %u len %d", id, dlen); | 420 | debug("request %u: sent data len %d", id, dlen); |
306 | send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); | 421 | send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); |
307 | } | 422 | } |
308 | 423 | ||
@@ -313,7 +428,7 @@ send_handle(u_int32_t id, int handle) | |||
313 | int hlen; | 428 | int hlen; |
314 | 429 | ||
315 | handle_to_string(handle, &string, &hlen); | 430 | handle_to_string(handle, &string, &hlen); |
316 | TRACE("sent handle id %u handle %d", id, handle); | 431 | debug("request %u: sent handle handle %d", id, handle); |
317 | send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); | 432 | send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); |
318 | xfree(string); | 433 | xfree(string); |
319 | } | 434 | } |
@@ -328,7 +443,7 @@ send_names(u_int32_t id, int count, const Stat *stats) | |||
328 | buffer_put_char(&msg, SSH2_FXP_NAME); | 443 | buffer_put_char(&msg, SSH2_FXP_NAME); |
329 | buffer_put_int(&msg, id); | 444 | buffer_put_int(&msg, id); |
330 | buffer_put_int(&msg, count); | 445 | buffer_put_int(&msg, count); |
331 | TRACE("sent names id %u count %d", id, count); | 446 | debug("request %u: sent names count %d", id, count); |
332 | for (i = 0; i < count; i++) { | 447 | for (i = 0; i < count; i++) { |
333 | buffer_put_cstring(&msg, stats[i].name); | 448 | buffer_put_cstring(&msg, stats[i].name); |
334 | buffer_put_cstring(&msg, stats[i].long_name); | 449 | buffer_put_cstring(&msg, stats[i].long_name); |
@@ -343,7 +458,7 @@ send_attrib(u_int32_t id, const Attrib *a) | |||
343 | { | 458 | { |
344 | Buffer msg; | 459 | Buffer msg; |
345 | 460 | ||
346 | TRACE("sent attrib id %u have 0x%x", id, a->flags); | 461 | debug("request %u: sent attrib have 0x%x", id, a->flags); |
347 | buffer_init(&msg); | 462 | buffer_init(&msg); |
348 | buffer_put_char(&msg, SSH2_FXP_ATTRS); | 463 | buffer_put_char(&msg, SSH2_FXP_ATTRS); |
349 | buffer_put_int(&msg, id); | 464 | buffer_put_int(&msg, id); |
@@ -360,7 +475,7 @@ process_init(void) | |||
360 | Buffer msg; | 475 | Buffer msg; |
361 | 476 | ||
362 | version = get_int(); | 477 | version = get_int(); |
363 | TRACE("client version %d", version); | 478 | verbose("received client version %d", version); |
364 | buffer_init(&msg); | 479 | buffer_init(&msg); |
365 | buffer_put_char(&msg, SSH2_FXP_VERSION); | 480 | buffer_put_char(&msg, SSH2_FXP_VERSION); |
366 | buffer_put_int(&msg, SSH2_FILEXFER_VERSION); | 481 | buffer_put_int(&msg, SSH2_FILEXFER_VERSION); |
@@ -379,10 +494,12 @@ process_open(void) | |||
379 | id = get_int(); | 494 | id = get_int(); |
380 | name = get_string(NULL); | 495 | name = get_string(NULL); |
381 | pflags = get_int(); /* portable flags */ | 496 | pflags = get_int(); /* portable flags */ |
497 | debug3("request %u: open flags %d", id, pflags); | ||
382 | a = get_attrib(); | 498 | a = get_attrib(); |
383 | flags = flags_from_portable(pflags); | 499 | flags = flags_from_portable(pflags); |
384 | mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; | 500 | mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; |
385 | TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode); | 501 | logit("open \"%s\" flags %s mode 0%o", |
502 | name, string_from_portable(pflags), mode); | ||
386 | fd = open(name, flags, mode); | 503 | fd = open(name, flags, mode); |
387 | if (fd < 0) { | 504 | if (fd < 0) { |
388 | status = errno_to_portable(errno); | 505 | status = errno_to_portable(errno); |
@@ -408,7 +525,8 @@ process_close(void) | |||
408 | 525 | ||
409 | id = get_int(); | 526 | id = get_int(); |
410 | handle = get_handle(); | 527 | handle = get_handle(); |
411 | TRACE("close id %u handle %d", id, handle); | 528 | debug3("request %u: close handle %u", id, handle); |
529 | handle_log_close(handle, NULL); | ||
412 | ret = handle_close(handle); | 530 | ret = handle_close(handle); |
413 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; | 531 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
414 | send_status(id, status); | 532 | send_status(id, status); |
@@ -427,11 +545,11 @@ process_read(void) | |||
427 | off = get_int64(); | 545 | off = get_int64(); |
428 | len = get_int(); | 546 | len = get_int(); |
429 | 547 | ||
430 | TRACE("read id %u handle %d off %llu len %d", id, handle, | 548 | debug("request %u: read \"%s\" (handle %d) off %llu len %d", |
431 | (unsigned long long)off, len); | 549 | id, handle_to_name(handle), handle, (unsigned long long)off, len); |
432 | if (len > sizeof buf) { | 550 | if (len > sizeof buf) { |
433 | len = sizeof buf; | 551 | len = sizeof buf; |
434 | logit("read change len %d", len); | 552 | debug2("read change len %d", len); |
435 | } | 553 | } |
436 | fd = handle_to_fd(handle); | 554 | fd = handle_to_fd(handle); |
437 | if (fd >= 0) { | 555 | if (fd >= 0) { |
@@ -447,6 +565,7 @@ process_read(void) | |||
447 | } else { | 565 | } else { |
448 | send_data(id, buf, ret); | 566 | send_data(id, buf, ret); |
449 | status = SSH2_FX_OK; | 567 | status = SSH2_FX_OK; |
568 | handle_update_read(handle, ret); | ||
450 | } | 569 | } |
451 | } | 570 | } |
452 | } | 571 | } |
@@ -468,8 +587,8 @@ process_write(void) | |||
468 | off = get_int64(); | 587 | off = get_int64(); |
469 | data = get_string(&len); | 588 | data = get_string(&len); |
470 | 589 | ||
471 | TRACE("write id %u handle %d off %llu len %d", id, handle, | 590 | debug("request %u: write \"%s\" (handle %d) off %llu len %d", |
472 | (unsigned long long)off, len); | 591 | id, handle_to_name(handle), handle, (unsigned long long)off, len); |
473 | fd = handle_to_fd(handle); | 592 | fd = handle_to_fd(handle); |
474 | if (fd >= 0) { | 593 | if (fd >= 0) { |
475 | if (lseek(fd, off, SEEK_SET) < 0) { | 594 | if (lseek(fd, off, SEEK_SET) < 0) { |
@@ -483,8 +602,9 @@ process_write(void) | |||
483 | status = errno_to_portable(errno); | 602 | status = errno_to_portable(errno); |
484 | } else if ((size_t)ret == len) { | 603 | } else if ((size_t)ret == len) { |
485 | status = SSH2_FX_OK; | 604 | status = SSH2_FX_OK; |
605 | handle_update_write(handle, ret); | ||
486 | } else { | 606 | } else { |
487 | logit("nothing at all written"); | 607 | debug2("nothing at all written"); |
488 | } | 608 | } |
489 | } | 609 | } |
490 | } | 610 | } |
@@ -503,7 +623,8 @@ process_do_stat(int do_lstat) | |||
503 | 623 | ||
504 | id = get_int(); | 624 | id = get_int(); |
505 | name = get_string(NULL); | 625 | name = get_string(NULL); |
506 | TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name); | 626 | debug3("request %u: %sstat", id, do_lstat ? "l" : ""); |
627 | verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); | ||
507 | ret = do_lstat ? lstat(name, &st) : stat(name, &st); | 628 | ret = do_lstat ? lstat(name, &st) : stat(name, &st); |
508 | if (ret < 0) { | 629 | if (ret < 0) { |
509 | status = errno_to_portable(errno); | 630 | status = errno_to_portable(errno); |
@@ -539,9 +660,10 @@ process_fstat(void) | |||
539 | 660 | ||
540 | id = get_int(); | 661 | id = get_int(); |
541 | handle = get_handle(); | 662 | handle = get_handle(); |
542 | TRACE("fstat id %u handle %d", id, handle); | 663 | debug("request %u: fstat \"%s\" (handle %u)", |
664 | id, handle_to_name(handle), handle); | ||
543 | fd = handle_to_fd(handle); | 665 | fd = handle_to_fd(handle); |
544 | if (fd >= 0) { | 666 | if (fd >= 0) { |
545 | ret = fstat(fd, &st); | 667 | ret = fstat(fd, &st); |
546 | if (ret < 0) { | 668 | if (ret < 0) { |
547 | status = errno_to_portable(errno); | 669 | status = errno_to_portable(errno); |
@@ -578,23 +700,33 @@ process_setstat(void) | |||
578 | id = get_int(); | 700 | id = get_int(); |
579 | name = get_string(NULL); | 701 | name = get_string(NULL); |
580 | a = get_attrib(); | 702 | a = get_attrib(); |
581 | TRACE("setstat id %u name %s", id, name); | 703 | debug("request %u: setstat name \"%s\"", id, name); |
582 | if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { | 704 | if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { |
705 | logit("set \"%s\" size %llu", name, a->size); | ||
583 | ret = truncate(name, a->size); | 706 | ret = truncate(name, a->size); |
584 | if (ret == -1) | 707 | if (ret == -1) |
585 | status = errno_to_portable(errno); | 708 | status = errno_to_portable(errno); |
586 | } | 709 | } |
587 | if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { | 710 | if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { |
711 | logit("set \"%s\" mode %04o", name, a->perm); | ||
588 | ret = chmod(name, a->perm & 0777); | 712 | ret = chmod(name, a->perm & 0777); |
589 | if (ret == -1) | 713 | if (ret == -1) |
590 | status = errno_to_portable(errno); | 714 | status = errno_to_portable(errno); |
591 | } | 715 | } |
592 | if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { | 716 | if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { |
717 | char buf[64]; | ||
718 | time_t t = a->mtime; | ||
719 | |||
720 | strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", | ||
721 | localtime(&t)); | ||
722 | logit("set \"%s\" modtime %s", name, buf); | ||
593 | ret = utimes(name, attrib_to_tv(a)); | 723 | ret = utimes(name, attrib_to_tv(a)); |
594 | if (ret == -1) | 724 | if (ret == -1) |
595 | status = errno_to_portable(errno); | 725 | status = errno_to_portable(errno); |
596 | } | 726 | } |
597 | if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { | 727 | if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { |
728 | logit("set \"%s\" owner %lu group %lu", name, | ||
729 | (u_long)a->uid, (u_long)a->gid); | ||
598 | ret = chown(name, a->uid, a->gid); | 730 | ret = chown(name, a->uid, a->gid); |
599 | if (ret == -1) | 731 | if (ret == -1) |
600 | status = errno_to_portable(errno); | 732 | status = errno_to_portable(errno); |
@@ -610,23 +742,25 @@ process_fsetstat(void) | |||
610 | u_int32_t id; | 742 | u_int32_t id; |
611 | int handle, fd, ret; | 743 | int handle, fd, ret; |
612 | int status = SSH2_FX_OK; | 744 | int status = SSH2_FX_OK; |
613 | char *name; | ||
614 | 745 | ||
615 | id = get_int(); | 746 | id = get_int(); |
616 | handle = get_handle(); | 747 | handle = get_handle(); |
617 | a = get_attrib(); | 748 | a = get_attrib(); |
618 | TRACE("fsetstat id %u handle %d", id, handle); | 749 | debug("request %u: fsetstat handle %d", id, handle); |
619 | fd = handle_to_fd(handle); | 750 | fd = handle_to_fd(handle); |
620 | name = handle_to_name(handle); | 751 | if (fd < 0) { |
621 | if (fd < 0 || name == NULL) { | ||
622 | status = SSH2_FX_FAILURE; | 752 | status = SSH2_FX_FAILURE; |
623 | } else { | 753 | } else { |
754 | char *name = handle_to_name(handle); | ||
755 | |||
624 | if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { | 756 | if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { |
757 | logit("set \"%s\" size %llu", name, a->size); | ||
625 | ret = ftruncate(fd, a->size); | 758 | ret = ftruncate(fd, a->size); |
626 | if (ret == -1) | 759 | if (ret == -1) |
627 | status = errno_to_portable(errno); | 760 | status = errno_to_portable(errno); |
628 | } | 761 | } |
629 | if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { | 762 | if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { |
763 | logit("set \"%s\" mode %04o", name, a->perm); | ||
630 | #ifdef HAVE_FCHMOD | 764 | #ifdef HAVE_FCHMOD |
631 | ret = fchmod(fd, a->perm & 0777); | 765 | ret = fchmod(fd, a->perm & 0777); |
632 | #else | 766 | #else |
@@ -636,6 +770,12 @@ process_fsetstat(void) | |||
636 | status = errno_to_portable(errno); | 770 | status = errno_to_portable(errno); |
637 | } | 771 | } |
638 | if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { | 772 | if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { |
773 | char buf[64]; | ||
774 | time_t t = a->mtime; | ||
775 | |||
776 | strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", | ||
777 | localtime(&t)); | ||
778 | logit("set \"%s\" modtime %s", name, buf); | ||
639 | #ifdef HAVE_FUTIMES | 779 | #ifdef HAVE_FUTIMES |
640 | ret = futimes(fd, attrib_to_tv(a)); | 780 | ret = futimes(fd, attrib_to_tv(a)); |
641 | #else | 781 | #else |
@@ -645,6 +785,8 @@ process_fsetstat(void) | |||
645 | status = errno_to_portable(errno); | 785 | status = errno_to_portable(errno); |
646 | } | 786 | } |
647 | if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { | 787 | if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { |
788 | logit("set \"%s\" owner %lu group %lu", name, | ||
789 | (u_long)a->uid, (u_long)a->gid); | ||
648 | #ifdef HAVE_FCHOWN | 790 | #ifdef HAVE_FCHOWN |
649 | ret = fchown(fd, a->uid, a->gid); | 791 | ret = fchown(fd, a->uid, a->gid); |
650 | #else | 792 | #else |
@@ -667,7 +809,8 @@ process_opendir(void) | |||
667 | 809 | ||
668 | id = get_int(); | 810 | id = get_int(); |
669 | path = get_string(NULL); | 811 | path = get_string(NULL); |
670 | TRACE("opendir id %u path %s", id, path); | 812 | debug3("request %u: opendir", id); |
813 | logit("opendir \"%s\"", path); | ||
671 | dirp = opendir(path); | 814 | dirp = opendir(path); |
672 | if (dirp == NULL) { | 815 | if (dirp == NULL) { |
673 | status = errno_to_portable(errno); | 816 | status = errno_to_portable(errno); |
@@ -697,22 +840,23 @@ process_readdir(void) | |||
697 | 840 | ||
698 | id = get_int(); | 841 | id = get_int(); |
699 | handle = get_handle(); | 842 | handle = get_handle(); |
700 | TRACE("readdir id %u handle %d", id, handle); | 843 | debug("request %u: readdir \"%s\" (handle %d)", id, |
844 | handle_to_name(handle), handle); | ||
701 | dirp = handle_to_dir(handle); | 845 | dirp = handle_to_dir(handle); |
702 | path = handle_to_name(handle); | 846 | path = handle_to_name(handle); |
703 | if (dirp == NULL || path == NULL) { | 847 | if (dirp == NULL || path == NULL) { |
704 | send_status(id, SSH2_FX_FAILURE); | 848 | send_status(id, SSH2_FX_FAILURE); |
705 | } else { | 849 | } else { |
706 | struct stat st; | 850 | struct stat st; |
707 | char pathname[1024]; | 851 | char pathname[MAXPATHLEN]; |
708 | Stat *stats; | 852 | Stat *stats; |
709 | int nstats = 10, count = 0, i; | 853 | int nstats = 10, count = 0, i; |
710 | 854 | ||
711 | stats = xmalloc(nstats * sizeof(Stat)); | 855 | stats = xcalloc(nstats, sizeof(Stat)); |
712 | while ((dp = readdir(dirp)) != NULL) { | 856 | while ((dp = readdir(dirp)) != NULL) { |
713 | if (count >= nstats) { | 857 | if (count >= nstats) { |
714 | nstats *= 2; | 858 | nstats *= 2; |
715 | stats = xrealloc(stats, nstats * sizeof(Stat)); | 859 | stats = xrealloc(stats, nstats, sizeof(Stat)); |
716 | } | 860 | } |
717 | /* XXX OVERFLOW ? */ | 861 | /* XXX OVERFLOW ? */ |
718 | snprintf(pathname, sizeof pathname, "%s%s%s", path, | 862 | snprintf(pathname, sizeof pathname, "%s%s%s", path, |
@@ -751,7 +895,8 @@ process_remove(void) | |||
751 | 895 | ||
752 | id = get_int(); | 896 | id = get_int(); |
753 | name = get_string(NULL); | 897 | name = get_string(NULL); |
754 | TRACE("remove id %u name %s", id, name); | 898 | debug3("request %u: remove", id); |
899 | logit("remove name \"%s\"", name); | ||
755 | ret = unlink(name); | 900 | ret = unlink(name); |
756 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; | 901 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
757 | send_status(id, status); | 902 | send_status(id, status); |
@@ -771,7 +916,8 @@ process_mkdir(void) | |||
771 | a = get_attrib(); | 916 | a = get_attrib(); |
772 | mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? | 917 | mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? |
773 | a->perm & 0777 : 0777; | 918 | a->perm & 0777 : 0777; |
774 | TRACE("mkdir id %u name %s mode 0%o", id, name, mode); | 919 | debug3("request %u: mkdir", id); |
920 | logit("mkdir name \"%s\" mode 0%o", name, mode); | ||
775 | ret = mkdir(name, mode); | 921 | ret = mkdir(name, mode); |
776 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; | 922 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
777 | send_status(id, status); | 923 | send_status(id, status); |
@@ -787,7 +933,8 @@ process_rmdir(void) | |||
787 | 933 | ||
788 | id = get_int(); | 934 | id = get_int(); |
789 | name = get_string(NULL); | 935 | name = get_string(NULL); |
790 | TRACE("rmdir id %u name %s", id, name); | 936 | debug3("request %u: rmdir", id); |
937 | logit("rmdir name \"%s\"", name); | ||
791 | ret = rmdir(name); | 938 | ret = rmdir(name); |
792 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; | 939 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
793 | send_status(id, status); | 940 | send_status(id, status); |
@@ -807,7 +954,8 @@ process_realpath(void) | |||
807 | xfree(path); | 954 | xfree(path); |
808 | path = xstrdup("."); | 955 | path = xstrdup("."); |
809 | } | 956 | } |
810 | TRACE("realpath id %u path %s", id, path); | 957 | debug3("request %u: realpath", id); |
958 | verbose("realpath \"%s\"", path); | ||
811 | if (realpath(path, resolvedname) == NULL) { | 959 | if (realpath(path, resolvedname) == NULL) { |
812 | send_status(id, errno_to_portable(errno)); | 960 | send_status(id, errno_to_portable(errno)); |
813 | } else { | 961 | } else { |
@@ -830,7 +978,8 @@ process_rename(void) | |||
830 | id = get_int(); | 978 | id = get_int(); |
831 | oldpath = get_string(NULL); | 979 | oldpath = get_string(NULL); |
832 | newpath = get_string(NULL); | 980 | newpath = get_string(NULL); |
833 | TRACE("rename id %u old %s new %s", id, oldpath, newpath); | 981 | debug3("request %u: rename", id); |
982 | logit("rename old \"%s\" new \"%s\"", oldpath, newpath); | ||
834 | status = SSH2_FX_FAILURE; | 983 | status = SSH2_FX_FAILURE; |
835 | if (lstat(oldpath, &sb) == -1) | 984 | if (lstat(oldpath, &sb) == -1) |
836 | status = errno_to_portable(errno); | 985 | status = errno_to_portable(errno); |
@@ -885,7 +1034,8 @@ process_readlink(void) | |||
885 | 1034 | ||
886 | id = get_int(); | 1035 | id = get_int(); |
887 | path = get_string(NULL); | 1036 | path = get_string(NULL); |
888 | TRACE("readlink id %u path %s", id, path); | 1037 | debug3("request %u: readlink", id); |
1038 | verbose("readlink \"%s\"", path); | ||
889 | if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) | 1039 | if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) |
890 | send_status(id, errno_to_portable(errno)); | 1040 | send_status(id, errno_to_portable(errno)); |
891 | else { | 1041 | else { |
@@ -909,7 +1059,8 @@ process_symlink(void) | |||
909 | id = get_int(); | 1059 | id = get_int(); |
910 | oldpath = get_string(NULL); | 1060 | oldpath = get_string(NULL); |
911 | newpath = get_string(NULL); | 1061 | newpath = get_string(NULL); |
912 | TRACE("symlink id %u old %s new %s", id, oldpath, newpath); | 1062 | debug3("request %u: symlink", id); |
1063 | logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); | ||
913 | /* this will fail if 'newpath' exists */ | 1064 | /* this will fail if 'newpath' exists */ |
914 | ret = symlink(oldpath, newpath); | 1065 | ret = symlink(oldpath, newpath); |
915 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; | 1066 | status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
@@ -945,10 +1096,11 @@ process(void) | |||
945 | if (buf_len < 5) | 1096 | if (buf_len < 5) |
946 | return; /* Incomplete message. */ | 1097 | return; /* Incomplete message. */ |
947 | cp = buffer_ptr(&iqueue); | 1098 | cp = buffer_ptr(&iqueue); |
948 | msg_len = GET_32BIT(cp); | 1099 | msg_len = get_u32(cp); |
949 | if (msg_len > SFTP_MAX_MSG_LENGTH) { | 1100 | if (msg_len > SFTP_MAX_MSG_LENGTH) { |
950 | error("bad message "); | 1101 | error("bad message from %s local user %s", |
951 | exit(11); | 1102 | client_addr, pw->pw_name); |
1103 | cleanup_exit(11); | ||
952 | } | 1104 | } |
953 | if (buf_len < msg_len + 4) | 1105 | if (buf_len < msg_len + 4) |
954 | return; | 1106 | return; |
@@ -1022,7 +1174,7 @@ process(void) | |||
1022 | } | 1174 | } |
1023 | /* discard the remaining bytes from the current packet */ | 1175 | /* discard the remaining bytes from the current packet */ |
1024 | if (buf_len < buffer_len(&iqueue)) | 1176 | if (buf_len < buffer_len(&iqueue)) |
1025 | fatal("iqueue grows"); | 1177 | fatal("iqueue grew unexpectedly"); |
1026 | consumed = buf_len - buffer_len(&iqueue); | 1178 | consumed = buf_len - buffer_len(&iqueue); |
1027 | if (msg_len < consumed) | 1179 | if (msg_len < consumed) |
1028 | fatal("msg_len %d < consumed %d", msg_len, consumed); | 1180 | fatal("msg_len %d < consumed %d", msg_len, consumed); |
@@ -1030,24 +1182,93 @@ process(void) | |||
1030 | buffer_consume(&iqueue, msg_len - consumed); | 1182 | buffer_consume(&iqueue, msg_len - consumed); |
1031 | } | 1183 | } |
1032 | 1184 | ||
1185 | /* Cleanup handler that logs active handles upon normal exit */ | ||
1186 | void | ||
1187 | cleanup_exit(int i) | ||
1188 | { | ||
1189 | if (pw != NULL && client_addr != NULL) { | ||
1190 | handle_log_exit(); | ||
1191 | logit("session closed for local user %s from [%s]", | ||
1192 | pw->pw_name, client_addr); | ||
1193 | } | ||
1194 | _exit(i); | ||
1195 | } | ||
1196 | |||
1197 | static void | ||
1198 | usage(void) | ||
1199 | { | ||
1200 | extern char *__progname; | ||
1201 | |||
1202 | fprintf(stderr, | ||
1203 | "usage: %s [-he] [-l log_level] [-f log_facility]\n", __progname); | ||
1204 | exit(1); | ||
1205 | } | ||
1206 | |||
1033 | int | 1207 | int |
1034 | main(int ac, char **av) | 1208 | main(int argc, char **argv) |
1035 | { | 1209 | { |
1036 | fd_set *rset, *wset; | 1210 | fd_set *rset, *wset; |
1037 | int in, out, max; | 1211 | int in, out, max, ch, skipargs = 0, log_stderr = 0; |
1038 | ssize_t len, olen, set_size; | 1212 | ssize_t len, olen, set_size; |
1213 | SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; | ||
1214 | char *cp; | ||
1215 | |||
1216 | extern char *optarg; | ||
1217 | extern char *__progname; | ||
1039 | 1218 | ||
1040 | /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ | 1219 | /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ |
1041 | sanitise_stdfd(); | 1220 | sanitise_stdfd(); |
1042 | 1221 | ||
1043 | /* XXX should use getopt */ | 1222 | __progname = ssh_get_progname(argv[0]); |
1223 | log_init(__progname, log_level, log_facility, log_stderr); | ||
1224 | |||
1225 | while (!skipargs && (ch = getopt(argc, argv, "C:f:l:che")) != -1) { | ||
1226 | switch (ch) { | ||
1227 | case 'c': | ||
1228 | /* | ||
1229 | * Ignore all arguments if we are invoked as a | ||
1230 | * shell using "sftp-server -c command" | ||
1231 | */ | ||
1232 | skipargs = 1; | ||
1233 | break; | ||
1234 | case 'e': | ||
1235 | log_stderr = 1; | ||
1236 | break; | ||
1237 | case 'l': | ||
1238 | log_level = log_level_number(optarg); | ||
1239 | if (log_level == SYSLOG_LEVEL_NOT_SET) | ||
1240 | error("Invalid log level \"%s\"", optarg); | ||
1241 | break; | ||
1242 | case 'f': | ||
1243 | log_facility = log_facility_number(optarg); | ||
1244 | if (log_level == SYSLOG_FACILITY_NOT_SET) | ||
1245 | error("Invalid log facility \"%s\"", optarg); | ||
1246 | break; | ||
1247 | case 'h': | ||
1248 | default: | ||
1249 | usage(); | ||
1250 | } | ||
1251 | } | ||
1044 | 1252 | ||
1045 | __progname = ssh_get_progname(av[0]); | 1253 | log_init(__progname, log_level, log_facility, log_stderr); |
1046 | handle_init(); | ||
1047 | 1254 | ||
1048 | #ifdef DEBUG_SFTP_SERVER | 1255 | if ((cp = getenv("SSH_CONNECTION")) != NULL) { |
1049 | log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); | 1256 | client_addr = xstrdup(cp); |
1050 | #endif | 1257 | if ((cp = strchr(client_addr, ' ')) == NULL) |
1258 | fatal("Malformed SSH_CONNECTION variable: \"%s\"", | ||
1259 | getenv("SSH_CONNECTION")); | ||
1260 | *cp = '\0'; | ||
1261 | } else | ||
1262 | client_addr = xstrdup("UNKNOWN"); | ||
1263 | |||
1264 | if ((pw = getpwuid(getuid())) == NULL) | ||
1265 | fatal("No user found for uid %lu", (u_long)getuid()); | ||
1266 | pw = pwcopy(pw); | ||
1267 | |||
1268 | logit("session opened for local user %s from [%s]", | ||
1269 | pw->pw_name, client_addr); | ||
1270 | |||
1271 | handle_init(); | ||
1051 | 1272 | ||
1052 | in = dup(STDIN_FILENO); | 1273 | in = dup(STDIN_FILENO); |
1053 | out = dup(STDOUT_FILENO); | 1274 | out = dup(STDOUT_FILENO); |
@@ -1082,7 +1303,8 @@ main(int ac, char **av) | |||
1082 | if (select(max+1, rset, wset, NULL, NULL) < 0) { | 1303 | if (select(max+1, rset, wset, NULL, NULL) < 0) { |
1083 | if (errno == EINTR) | 1304 | if (errno == EINTR) |
1084 | continue; | 1305 | continue; |
1085 | exit(2); | 1306 | error("select: %s", strerror(errno)); |
1307 | cleanup_exit(2); | ||
1086 | } | 1308 | } |
1087 | 1309 | ||
1088 | /* copy stdin to iqueue */ | 1310 | /* copy stdin to iqueue */ |
@@ -1091,10 +1313,10 @@ main(int ac, char **av) | |||
1091 | len = read(in, buf, sizeof buf); | 1313 | len = read(in, buf, sizeof buf); |
1092 | if (len == 0) { | 1314 | if (len == 0) { |
1093 | debug("read eof"); | 1315 | debug("read eof"); |
1094 | exit(0); | 1316 | cleanup_exit(0); |
1095 | } else if (len < 0) { | 1317 | } else if (len < 0) { |
1096 | error("read error"); | 1318 | error("read: %s", strerror(errno)); |
1097 | exit(1); | 1319 | cleanup_exit(1); |
1098 | } else { | 1320 | } else { |
1099 | buffer_append(&iqueue, buf, len); | 1321 | buffer_append(&iqueue, buf, len); |
1100 | } | 1322 | } |
@@ -1103,8 +1325,8 @@ main(int ac, char **av) | |||
1103 | if (FD_ISSET(out, wset)) { | 1325 | if (FD_ISSET(out, wset)) { |
1104 | len = write(out, buffer_ptr(&oqueue), olen); | 1326 | len = write(out, buffer_ptr(&oqueue), olen); |
1105 | if (len < 0) { | 1327 | if (len < 0) { |
1106 | error("write error"); | 1328 | error("write: %s", strerror(errno)); |
1107 | exit(1); | 1329 | cleanup_exit(1); |
1108 | } else { | 1330 | } else { |
1109 | buffer_consume(&oqueue, len); | 1331 | buffer_consume(&oqueue, len); |
1110 | } | 1332 | } |