|
Lines 26-31
Link Here
|
| 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" |
|
Lines 34-40
Link Here
|
| 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 |
|
|
|
39 |
/* Our verbosity */ |
| 40 |
LogLevel log_level = SYSLOG_LEVEL_ERROR; |
| 41 |
|
| 42 |
/* Our client */ |
| 43 |
struct passwd *pw; |
| 44 |
char *client_addr; |
| 38 |
|
45 |
|
| 39 |
/* input and output queue */ |
46 |
/* input and output queue */ |
| 40 |
Buffer iqueue; |
47 |
Buffer iqueue; |
|
Lines 106-111
flags_from_portable(int pflags)
Link Here
|
| 106 |
return flags; |
113 |
return flags; |
| 107 |
} |
114 |
} |
| 108 |
|
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 |
|
| 109 |
static Attrib * |
143 |
static Attrib * |
| 110 |
get_attrib(void) |
144 |
get_attrib(void) |
| 111 |
{ |
145 |
{ |
|
Lines 120-125
struct Handle {
Link Here
|
| 120 |
DIR *dirp; |
154 |
DIR *dirp; |
| 121 |
int fd; |
155 |
int fd; |
| 122 |
char *name; |
156 |
char *name; |
|
|
157 |
u_int64_t bytes_read, bytes_write; |
| 123 |
}; |
158 |
}; |
| 124 |
|
159 |
|
| 125 |
enum { |
160 |
enum { |
|
Lines 150-155
handle_new(int use, const char *name, in
Link Here
|
| 150 |
handles[i].dirp = dirp; |
185 |
handles[i].dirp = dirp; |
| 151 |
handles[i].fd = fd; |
186 |
handles[i].fd = fd; |
| 152 |
handles[i].name = xstrdup(name); |
187 |
handles[i].name = xstrdup(name); |
|
|
188 |
handles[i].bytes_read = handles[i].bytes_write = 0; |
| 153 |
return i; |
189 |
return i; |
| 154 |
} |
190 |
} |
| 155 |
} |
191 |
} |
|
Lines 213-218
handle_to_fd(int handle)
Link Here
|
| 213 |
return -1; |
249 |
return -1; |
| 214 |
} |
250 |
} |
| 215 |
|
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 |
|
| 216 |
static int |
282 |
static int |
| 217 |
handle_close(int handle) |
283 |
handle_close(int handle) |
| 218 |
{ |
284 |
{ |
|
Lines 232-237
handle_close(int handle)
Link Here
|
| 232 |
return ret; |
298 |
return ret; |
| 233 |
} |
299 |
} |
| 234 |
|
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 |
|
| 235 |
static int |
326 |
static int |
| 236 |
get_handle(void) |
327 |
get_handle(void) |
| 237 |
{ |
328 |
{ |
|
Lines 258-267
send_msg(Buffer *m)
Link Here
|
| 258 |
buffer_consume(m, mlen); |
349 |
buffer_consume(m, mlen); |
| 259 |
} |
350 |
} |
| 260 |
|
351 |
|
| 261 |
static void |
352 |
static const char * |
| 262 |
send_status(u_int32_t id, u_int32_t status) |
353 |
status_to_message(u_int32_t status) |
| 263 |
{ |
354 |
{ |
| 264 |
Buffer msg; |
|
|
| 265 |
const char *status_messages[] = { |
355 |
const char *status_messages[] = { |
| 266 |
"Success", /* SSH_FX_OK */ |
356 |
"Success", /* SSH_FX_OK */ |
| 267 |
"End of file", /* SSH_FX_EOF */ |
357 |
"End of file", /* SSH_FX_EOF */ |
|
Lines 274-288
send_status(u_int32_t id, u_int32_t stat
Link Here
|
| 274 |
"Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ |
364 |
"Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ |
| 275 |
"Unknown error" /* Others */ |
365 |
"Unknown error" /* Others */ |
| 276 |
}; |
366 |
}; |
|
|
367 |
return (status_messages[MIN(status,SSH2_FX_MAX)]); |
| 368 |
} |
| 369 |
|
| 370 |
static void |
| 371 |
send_status(u_int32_t id, u_int32_t status) |
| 372 |
{ |
| 373 |
Buffer msg; |
| 277 |
|
374 |
|
| 278 |
TRACE("sent status id %u error %u", id, status); |
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)); |
| 279 |
buffer_init(&msg); |
379 |
buffer_init(&msg); |
| 280 |
buffer_put_char(&msg, SSH2_FXP_STATUS); |
380 |
buffer_put_char(&msg, SSH2_FXP_STATUS); |
| 281 |
buffer_put_int(&msg, id); |
381 |
buffer_put_int(&msg, id); |
| 282 |
buffer_put_int(&msg, status); |
382 |
buffer_put_int(&msg, status); |
| 283 |
if (version >= 3) { |
383 |
if (version >= 3) { |
| 284 |
buffer_put_cstring(&msg, |
384 |
buffer_put_cstring(&msg, status_to_message(status)); |
| 285 |
status_messages[MIN(status,SSH2_FX_MAX)]); |
|
|
| 286 |
buffer_put_cstring(&msg, ""); |
385 |
buffer_put_cstring(&msg, ""); |
| 287 |
} |
386 |
} |
| 288 |
send_msg(&msg); |
387 |
send_msg(&msg); |
|
Lines 304-310
send_data_or_handle(char type, u_int32_t
Link Here
|
| 304 |
static void |
403 |
static void |
| 305 |
send_data(u_int32_t id, const char *data, int dlen) |
404 |
send_data(u_int32_t id, const char *data, int dlen) |
| 306 |
{ |
405 |
{ |
| 307 |
TRACE("sent data id %u len %d", id, dlen); |
406 |
debug("request %u: sent data len %d", id, dlen); |
| 308 |
send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); |
407 |
send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); |
| 309 |
} |
408 |
} |
| 310 |
|
409 |
|
|
Lines 315-321
send_handle(u_int32_t id, int handle)
Link Here
|
| 315 |
int hlen; |
414 |
int hlen; |
| 316 |
|
415 |
|
| 317 |
handle_to_string(handle, &string, &hlen); |
416 |
handle_to_string(handle, &string, &hlen); |
| 318 |
TRACE("sent handle id %u handle %d", id, handle); |
417 |
debug("request %u: sent handle handle %d", id, handle); |
| 319 |
send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); |
418 |
send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); |
| 320 |
xfree(string); |
419 |
xfree(string); |
| 321 |
} |
420 |
} |
|
Lines 330-336
send_names(u_int32_t id, int count, cons
Link Here
|
| 330 |
buffer_put_char(&msg, SSH2_FXP_NAME); |
429 |
buffer_put_char(&msg, SSH2_FXP_NAME); |
| 331 |
buffer_put_int(&msg, id); |
430 |
buffer_put_int(&msg, id); |
| 332 |
buffer_put_int(&msg, count); |
431 |
buffer_put_int(&msg, count); |
| 333 |
TRACE("sent names id %u count %d", id, count); |
432 |
debug("request %u: sent names count %d", id, count); |
| 334 |
for (i = 0; i < count; i++) { |
433 |
for (i = 0; i < count; i++) { |
| 335 |
buffer_put_cstring(&msg, stats[i].name); |
434 |
buffer_put_cstring(&msg, stats[i].name); |
| 336 |
buffer_put_cstring(&msg, stats[i].long_name); |
435 |
buffer_put_cstring(&msg, stats[i].long_name); |
|
Lines 345-351
send_attrib(u_int32_t id, const Attrib *
Link Here
|
| 345 |
{ |
444 |
{ |
| 346 |
Buffer msg; |
445 |
Buffer msg; |
| 347 |
|
446 |
|
| 348 |
TRACE("sent attrib id %u have 0x%x", id, a->flags); |
447 |
debug("request %u: sent attrib have 0x%x", id, a->flags); |
| 349 |
buffer_init(&msg); |
448 |
buffer_init(&msg); |
| 350 |
buffer_put_char(&msg, SSH2_FXP_ATTRS); |
449 |
buffer_put_char(&msg, SSH2_FXP_ATTRS); |
| 351 |
buffer_put_int(&msg, id); |
450 |
buffer_put_int(&msg, id); |
|
Lines 362-368
process_init(void)
Link Here
|
| 362 |
Buffer msg; |
461 |
Buffer msg; |
| 363 |
|
462 |
|
| 364 |
version = get_int(); |
463 |
version = get_int(); |
| 365 |
TRACE("client version %d", version); |
464 |
verbose("received client version %d", version); |
| 366 |
buffer_init(&msg); |
465 |
buffer_init(&msg); |
| 367 |
buffer_put_char(&msg, SSH2_FXP_VERSION); |
466 |
buffer_put_char(&msg, SSH2_FXP_VERSION); |
| 368 |
buffer_put_int(&msg, SSH2_FILEXFER_VERSION); |
467 |
buffer_put_int(&msg, SSH2_FILEXFER_VERSION); |
|
Lines 379-390
process_open(void)
Link Here
|
| 379 |
int handle, fd, flags, mode, status = SSH2_FX_FAILURE; |
478 |
int handle, fd, flags, mode, status = SSH2_FX_FAILURE; |
| 380 |
|
479 |
|
| 381 |
id = get_int(); |
480 |
id = get_int(); |
|
|
481 |
debug3("request %u: open flags %d", id, pflags); |
| 382 |
name = get_string(NULL); |
482 |
name = get_string(NULL); |
| 383 |
pflags = get_int(); /* portable flags */ |
483 |
pflags = get_int(); /* portable flags */ |
| 384 |
a = get_attrib(); |
484 |
a = get_attrib(); |
| 385 |
flags = flags_from_portable(pflags); |
485 |
flags = flags_from_portable(pflags); |
| 386 |
mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; |
486 |
mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; |
| 387 |
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); |
| 388 |
fd = open(name, flags, mode); |
489 |
fd = open(name, flags, mode); |
| 389 |
if (fd < 0) { |
490 |
if (fd < 0) { |
| 390 |
status = errno_to_portable(errno); |
491 |
status = errno_to_portable(errno); |
|
Lines 410-416
process_close(void)
Link Here
|
| 410 |
|
511 |
|
| 411 |
id = get_int(); |
512 |
id = get_int(); |
| 412 |
handle = get_handle(); |
513 |
handle = get_handle(); |
| 413 |
TRACE("close id %u handle %d", id, handle); |
514 |
debug3("request %u: close handle %u", id, handle); |
|
|
515 |
handle_log_close(handle, NULL); |
| 414 |
ret = handle_close(handle); |
516 |
ret = handle_close(handle); |
| 415 |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
517 |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
| 416 |
send_status(id, status); |
518 |
send_status(id, status); |
|
Lines 429-439
process_read(void)
Link Here
|
| 429 |
off = get_int64(); |
531 |
off = get_int64(); |
| 430 |
len = get_int(); |
532 |
len = get_int(); |
| 431 |
|
533 |
|
| 432 |
TRACE("read id %u handle %d off %llu len %d", id, handle, |
534 |
debug("request %u: read \"%s\" (handle %d) off %llu len %d", |
| 433 |
(unsigned long long)off, len); |
535 |
id, handle_to_name(handle), handle, (unsigned long long)off, len); |
| 434 |
if (len > sizeof buf) { |
536 |
if (len > sizeof buf) { |
| 435 |
len = sizeof buf; |
537 |
len = sizeof buf; |
| 436 |
logit("read change len %d", len); |
538 |
debug2("read change len %d", len); |
| 437 |
} |
539 |
} |
| 438 |
fd = handle_to_fd(handle); |
540 |
fd = handle_to_fd(handle); |
| 439 |
if (fd >= 0) { |
541 |
if (fd >= 0) { |
|
Lines 449-454
process_read(void)
Link Here
|
| 449 |
} else { |
551 |
} else { |
| 450 |
send_data(id, buf, ret); |
552 |
send_data(id, buf, ret); |
| 451 |
status = SSH2_FX_OK; |
553 |
status = SSH2_FX_OK; |
|
|
554 |
handle_update_read(handle, ret); |
| 452 |
} |
555 |
} |
| 453 |
} |
556 |
} |
| 454 |
} |
557 |
} |
|
Lines 470-477
process_write(void)
Link Here
|
| 470 |
off = get_int64(); |
573 |
off = get_int64(); |
| 471 |
data = get_string(&len); |
574 |
data = get_string(&len); |
| 472 |
|
575 |
|
| 473 |
TRACE("write id %u handle %d off %llu len %d", id, handle, |
576 |
debug("request %u: write \"%s\" (handle %d) off %llu len %d", |
| 474 |
(unsigned long long)off, len); |
577 |
id, handle_to_name(handle), handle, (unsigned long long)off, len); |
| 475 |
fd = handle_to_fd(handle); |
578 |
fd = handle_to_fd(handle); |
| 476 |
if (fd >= 0) { |
579 |
if (fd >= 0) { |
| 477 |
if (lseek(fd, off, SEEK_SET) < 0) { |
580 |
if (lseek(fd, off, SEEK_SET) < 0) { |
|
Lines 485-492
process_write(void)
Link Here
|
| 485 |
status = errno_to_portable(errno); |
588 |
status = errno_to_portable(errno); |
| 486 |
} else if ((size_t)ret == len) { |
589 |
} else if ((size_t)ret == len) { |
| 487 |
status = SSH2_FX_OK; |
590 |
status = SSH2_FX_OK; |
|
|
591 |
handle_update_write(handle, ret); |
| 488 |
} else { |
592 |
} else { |
| 489 |
logit("nothing at all written"); |
593 |
debug2("nothing at all written"); |
| 490 |
} |
594 |
} |
| 491 |
} |
595 |
} |
| 492 |
} |
596 |
} |
|
Lines 505-511
process_do_stat(int do_lstat)
Link Here
|
| 505 |
|
609 |
|
| 506 |
id = get_int(); |
610 |
id = get_int(); |
| 507 |
name = get_string(NULL); |
611 |
name = get_string(NULL); |
| 508 |
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); |
| 509 |
ret = do_lstat ? lstat(name, &st) : stat(name, &st); |
614 |
ret = do_lstat ? lstat(name, &st) : stat(name, &st); |
| 510 |
if (ret < 0) { |
615 |
if (ret < 0) { |
| 511 |
status = errno_to_portable(errno); |
616 |
status = errno_to_portable(errno); |
|
Lines 541-547
process_fstat(void)
Link Here
|
| 541 |
|
646 |
|
| 542 |
id = get_int(); |
647 |
id = get_int(); |
| 543 |
handle = get_handle(); |
648 |
handle = get_handle(); |
| 544 |
TRACE("fstat id %u handle %d", id, handle); |
649 |
debug("request %u: fstat \"%s\" (handle %u)", |
|
|
650 |
id, handle_to_name(handle), handle); |
| 545 |
fd = handle_to_fd(handle); |
651 |
fd = handle_to_fd(handle); |
| 546 |
if (fd >= 0) { |
652 |
if (fd >= 0) { |
| 547 |
ret = fstat(fd, &st); |
653 |
ret = fstat(fd, &st); |
|
Lines 580-602
process_setstat(void)
Link Here
|
| 580 |
id = get_int(); |
686 |
id = get_int(); |
| 581 |
name = get_string(NULL); |
687 |
name = get_string(NULL); |
| 582 |
a = get_attrib(); |
688 |
a = get_attrib(); |
| 583 |
TRACE("setstat id %u name %s", id, name); |
689 |
debug("request %u: setstat name \"%s\"", id, name); |
| 584 |
if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { |
690 |
if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { |
|
|
691 |
logit("set \"%s\" size %llu", name, a->size); |
| 585 |
ret = truncate(name, a->size); |
692 |
ret = truncate(name, a->size); |
| 586 |
if (ret == -1) |
693 |
if (ret == -1) |
| 587 |
status = errno_to_portable(errno); |
694 |
status = errno_to_portable(errno); |
| 588 |
} |
695 |
} |
| 589 |
if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { |
696 |
if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { |
|
|
697 |
logit("set \"%s\" mode %04o", name, a->perm); |
| 590 |
ret = chmod(name, a->perm & 0777); |
698 |
ret = chmod(name, a->perm & 0777); |
| 591 |
if (ret == -1) |
699 |
if (ret == -1) |
| 592 |
status = errno_to_portable(errno); |
700 |
status = errno_to_portable(errno); |
| 593 |
} |
701 |
} |
| 594 |
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); |
| 595 |
ret = utimes(name, attrib_to_tv(a)); |
709 |
ret = utimes(name, attrib_to_tv(a)); |
| 596 |
if (ret == -1) |
710 |
if (ret == -1) |
| 597 |
status = errno_to_portable(errno); |
711 |
status = errno_to_portable(errno); |
| 598 |
} |
712 |
} |
| 599 |
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); |
| 600 |
ret = chown(name, a->uid, a->gid); |
716 |
ret = chown(name, a->uid, a->gid); |
| 601 |
if (ret == -1) |
717 |
if (ret == -1) |
| 602 |
status = errno_to_portable(errno); |
718 |
status = errno_to_portable(errno); |
|
Lines 616-642
process_fsetstat(void)
Link Here
|
| 616 |
id = get_int(); |
732 |
id = get_int(); |
| 617 |
handle = get_handle(); |
733 |
handle = get_handle(); |
| 618 |
a = get_attrib(); |
734 |
a = get_attrib(); |
| 619 |
TRACE("fsetstat id %u handle %d", id, handle); |
735 |
debug("request %u: fsetstat handle %d", id, handle); |
| 620 |
fd = handle_to_fd(handle); |
736 |
fd = handle_to_fd(handle); |
| 621 |
if (fd < 0) { |
737 |
if (fd < 0) { |
| 622 |
status = SSH2_FX_FAILURE; |
738 |
status = SSH2_FX_FAILURE; |
| 623 |
} else { |
739 |
} else { |
|
|
740 |
char *name = handle_to_name(handle); |
| 741 |
|
| 624 |
if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { |
742 |
if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { |
|
|
743 |
logit("set \"%s\" size %llu", name, a->size); |
| 625 |
ret = ftruncate(fd, a->size); |
744 |
ret = ftruncate(fd, a->size); |
| 626 |
if (ret == -1) |
745 |
if (ret == -1) |
| 627 |
status = errno_to_portable(errno); |
746 |
status = errno_to_portable(errno); |
| 628 |
} |
747 |
} |
| 629 |
if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { |
748 |
if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { |
|
|
749 |
logit("set \"%s\" mode %04o", name, a->perm); |
| 630 |
ret = fchmod(fd, a->perm & 0777); |
750 |
ret = fchmod(fd, a->perm & 0777); |
| 631 |
if (ret == -1) |
751 |
if (ret == -1) |
| 632 |
status = errno_to_portable(errno); |
752 |
status = errno_to_portable(errno); |
| 633 |
} |
753 |
} |
| 634 |
if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { |
754 |
if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { |
|
|
755 |
char buf[64]; |
| 756 |
time_t t = a->mtime; |
| 757 |
|
| 758 |
strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", |
| 759 |
localtime(&t)); |
| 760 |
logit("set \"%s\" modtime %s", name, buf); |
| 635 |
ret = futimes(fd, attrib_to_tv(a)); |
761 |
ret = futimes(fd, attrib_to_tv(a)); |
| 636 |
if (ret == -1) |
762 |
if (ret == -1) |
| 637 |
status = errno_to_portable(errno); |
763 |
status = errno_to_portable(errno); |
| 638 |
} |
764 |
} |
| 639 |
if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { |
765 |
if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { |
|
|
766 |
logit("set \"%s\" owner %lu group %lu", name, |
| 767 |
(u_long)a->uid, (u_long)a->gid); |
| 640 |
ret = fchown(fd, a->uid, a->gid); |
768 |
ret = fchown(fd, a->uid, a->gid); |
| 641 |
if (ret == -1) |
769 |
if (ret == -1) |
| 642 |
status = errno_to_portable(errno); |
770 |
status = errno_to_portable(errno); |
|
Lines 655-661
process_opendir(void)
Link Here
|
| 655 |
|
783 |
|
| 656 |
id = get_int(); |
784 |
id = get_int(); |
| 657 |
path = get_string(NULL); |
785 |
path = get_string(NULL); |
| 658 |
TRACE("opendir id %u path %s", id, path); |
786 |
debug3("request %u: opendir", id); |
|
|
787 |
logit("opendir \"%s\"", path); |
| 659 |
dirp = opendir(path); |
788 |
dirp = opendir(path); |
| 660 |
if (dirp == NULL) { |
789 |
if (dirp == NULL) { |
| 661 |
status = errno_to_portable(errno); |
790 |
status = errno_to_portable(errno); |
|
Lines 685-698
process_readdir(void)
Link Here
|
| 685 |
|
814 |
|
| 686 |
id = get_int(); |
815 |
id = get_int(); |
| 687 |
handle = get_handle(); |
816 |
handle = get_handle(); |
| 688 |
TRACE("readdir id %u handle %d", id, handle); |
817 |
debug("request %u: readdir \"%s\" (handle %d)", id, |
|
|
818 |
handle_to_name(handle), handle); |
| 689 |
dirp = handle_to_dir(handle); |
819 |
dirp = handle_to_dir(handle); |
| 690 |
path = handle_to_name(handle); |
820 |
path = handle_to_name(handle); |
| 691 |
if (dirp == NULL || path == NULL) { |
821 |
if (dirp == NULL || path == NULL) { |
| 692 |
send_status(id, SSH2_FX_FAILURE); |
822 |
send_status(id, SSH2_FX_FAILURE); |
| 693 |
} else { |
823 |
} else { |
| 694 |
struct stat st; |
824 |
struct stat st; |
| 695 |
char pathname[1024]; |
825 |
char pathname[MAXPATHLEN]; |
| 696 |
Stat *stats; |
826 |
Stat *stats; |
| 697 |
int nstats = 10, count = 0, i; |
827 |
int nstats = 10, count = 0, i; |
| 698 |
|
828 |
|
|
Lines 739-745
process_remove(void)
Link Here
|
| 739 |
|
869 |
|
| 740 |
id = get_int(); |
870 |
id = get_int(); |
| 741 |
name = get_string(NULL); |
871 |
name = get_string(NULL); |
| 742 |
TRACE("remove id %u name %s", id, name); |
872 |
debug3("request %u: remove", id); |
|
|
873 |
logit("remove name \"%s\"", name); |
| 743 |
ret = unlink(name); |
874 |
ret = unlink(name); |
| 744 |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
875 |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
| 745 |
send_status(id, status); |
876 |
send_status(id, status); |
|
Lines 759-765
process_mkdir(void)
Link Here
|
| 759 |
a = get_attrib(); |
890 |
a = get_attrib(); |
| 760 |
mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? |
891 |
mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? |
| 761 |
a->perm & 0777 : 0777; |
892 |
a->perm & 0777 : 0777; |
| 762 |
TRACE("mkdir id %u name %s mode 0%o", id, name, mode); |
893 |
debug3("request %u: mkdir", id); |
|
|
894 |
logit("mkdir name \"%s\" mode 0%o", name, mode); |
| 763 |
ret = mkdir(name, mode); |
895 |
ret = mkdir(name, mode); |
| 764 |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
896 |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
| 765 |
send_status(id, status); |
897 |
send_status(id, status); |
|
Lines 775-781
process_rmdir(void)
Link Here
|
| 775 |
|
907 |
|
| 776 |
id = get_int(); |
908 |
id = get_int(); |
| 777 |
name = get_string(NULL); |
909 |
name = get_string(NULL); |
| 778 |
TRACE("rmdir id %u name %s", id, name); |
910 |
debug3("request %u: rmdir", id); |
|
|
911 |
logit("rmdir name \"%s\"", name); |
| 779 |
ret = rmdir(name); |
912 |
ret = rmdir(name); |
| 780 |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
913 |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
| 781 |
send_status(id, status); |
914 |
send_status(id, status); |
|
Lines 795-801
process_realpath(void)
Link Here
|
| 795 |
xfree(path); |
928 |
xfree(path); |
| 796 |
path = xstrdup("."); |
929 |
path = xstrdup("."); |
| 797 |
} |
930 |
} |
| 798 |
TRACE("realpath id %u path %s", id, path); |
931 |
debug3("request %u: realpath", id); |
|
|
932 |
verbose("realpath \"%s\"", path); |
| 799 |
if (realpath(path, resolvedname) == NULL) { |
933 |
if (realpath(path, resolvedname) == NULL) { |
| 800 |
send_status(id, errno_to_portable(errno)); |
934 |
send_status(id, errno_to_portable(errno)); |
| 801 |
} else { |
935 |
} else { |
|
Lines 818-824
process_rename(void)
Link Here
|
| 818 |
id = get_int(); |
952 |
id = get_int(); |
| 819 |
oldpath = get_string(NULL); |
953 |
oldpath = get_string(NULL); |
| 820 |
newpath = get_string(NULL); |
954 |
newpath = get_string(NULL); |
| 821 |
TRACE("rename id %u old %s new %s", id, oldpath, newpath); |
955 |
debug3("request %u: rename", id); |
|
|
956 |
logit("rename old \"%s\" new \"%s\"", oldpath, newpath); |
| 822 |
status = SSH2_FX_FAILURE; |
957 |
status = SSH2_FX_FAILURE; |
| 823 |
if (lstat(oldpath, &sb) == -1) |
958 |
if (lstat(oldpath, &sb) == -1) |
| 824 |
status = errno_to_portable(errno); |
959 |
status = errno_to_portable(errno); |
|
Lines 869-875
process_readlink(void)
Link Here
|
| 869 |
|
1004 |
|
| 870 |
id = get_int(); |
1005 |
id = get_int(); |
| 871 |
path = get_string(NULL); |
1006 |
path = get_string(NULL); |
| 872 |
TRACE("readlink id %u path %s", id, path); |
1007 |
debug3("request %u: readlink", id); |
|
|
1008 |
verbose("readlink \"%s\"", path); |
| 873 |
if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) |
1009 |
if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) |
| 874 |
send_status(id, errno_to_portable(errno)); |
1010 |
send_status(id, errno_to_portable(errno)); |
| 875 |
else { |
1011 |
else { |
|
Lines 893-899
process_symlink(void)
Link Here
|
| 893 |
id = get_int(); |
1029 |
id = get_int(); |
| 894 |
oldpath = get_string(NULL); |
1030 |
oldpath = get_string(NULL); |
| 895 |
newpath = get_string(NULL); |
1031 |
newpath = get_string(NULL); |
| 896 |
TRACE("symlink id %u old %s new %s", id, oldpath, newpath); |
1032 |
debug3("request %u: symlink", id); |
|
|
1033 |
logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); |
| 897 |
/* this will fail if 'newpath' exists */ |
1034 |
/* this will fail if 'newpath' exists */ |
| 898 |
ret = symlink(oldpath, newpath); |
1035 |
ret = symlink(oldpath, newpath); |
| 899 |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
1036 |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
|
Lines 931-938
process(void)
Link Here
|
| 931 |
cp = buffer_ptr(&iqueue); |
1068 |
cp = buffer_ptr(&iqueue); |
| 932 |
msg_len = get_u32(cp); |
1069 |
msg_len = get_u32(cp); |
| 933 |
if (msg_len > SFTP_MAX_MSG_LENGTH) { |
1070 |
if (msg_len > SFTP_MAX_MSG_LENGTH) { |
| 934 |
error("bad message "); |
1071 |
error("bad message from %s local user %s", |
| 935 |
exit(11); |
1072 |
client_addr, pw->pw_name); |
|
|
1073 |
cleanup_exit(11); |
| 936 |
} |
1074 |
} |
| 937 |
if (buf_len < msg_len + 4) |
1075 |
if (buf_len < msg_len + 4) |
| 938 |
return; |
1076 |
return; |
|
Lines 1006-1036
process(void)
Link Here
|
| 1006 |
} |
1144 |
} |
| 1007 |
/* discard the remaining bytes from the current packet */ |
1145 |
/* discard the remaining bytes from the current packet */ |
| 1008 |
if (buf_len < buffer_len(&iqueue)) |
1146 |
if (buf_len < buffer_len(&iqueue)) |
| 1009 |
fatal("iqueue grows"); |
1147 |
fatal("iqueue grew unexpectedly (client %s local user %s)", |
|
|
1148 |
client_addr, pw->pw_name); |
| 1010 |
consumed = buf_len - buffer_len(&iqueue); |
1149 |
consumed = buf_len - buffer_len(&iqueue); |
| 1011 |
if (msg_len < consumed) |
1150 |
if (msg_len < consumed) |
| 1012 |
fatal("msg_len %d < consumed %d", msg_len, consumed); |
1151 |
fatal("msg_len %d < consumed %d (client %s local user %s)", |
|
|
1152 |
msg_len, consumed, client_addr, pw->pw_name); |
| 1013 |
if (msg_len > consumed) |
1153 |
if (msg_len > consumed) |
| 1014 |
buffer_consume(&iqueue, msg_len - consumed); |
1154 |
buffer_consume(&iqueue, msg_len - consumed); |
| 1015 |
} |
1155 |
} |
| 1016 |
|
1156 |
|
|
|
1157 |
static void |
| 1158 |
do_chroot(const char *chroot_path_template) |
| 1159 |
{ |
| 1160 |
char *cp, *chroot_path; |
| 1161 |
struct group *gr; |
| 1162 |
|
| 1163 |
if ((gr = getgrgid(pw->pw_gid)) == NULL) |
| 1164 |
fatal("No group found for gid %lu", (u_long)pw->pw_gid); |
| 1165 |
|
| 1166 |
cp = percent_expand(chroot_path_template, "d", pw->pw_dir, |
| 1167 |
"u", pw->pw_name, "g", gr->gr_name, (char *)NULL); |
| 1168 |
chroot_path = tilde_expand_filename(cp, getuid()); |
| 1169 |
xfree(cp); |
| 1170 |
|
| 1171 |
logit("chroot to %s", chroot_path); |
| 1172 |
|
| 1173 |
/* Ensure the user has rights to access the chroot path first */ |
| 1174 |
temporarily_use_uid(pw); |
| 1175 |
if (chdir(chroot_path) == -1) |
| 1176 |
fatal("chdir(\"%s\"): %s", chroot_path, strerror(errno)); |
| 1177 |
restore_uid(); |
| 1178 |
|
| 1179 |
if (chroot(chroot_path) == -1) |
| 1180 |
fatal("chroot(\"%s\"): %s", chroot_path, strerror(errno)); |
| 1181 |
if (chdir("/") == -1) |
| 1182 |
fatal("chdir(\"/\"): %s", strerror(errno)); |
| 1183 |
xfree(chroot_path); |
| 1184 |
} |
| 1185 |
|
| 1186 |
/* Cleanup handler that logs active handles upon normal exit */ |
| 1187 |
void |
| 1188 |
cleanup_exit(int i) |
| 1189 |
{ |
| 1190 |
handle_log_exit(); |
| 1191 |
_exit(i); |
| 1192 |
} |
| 1193 |
|
| 1194 |
static void |
| 1195 |
usage(void) |
| 1196 |
{ |
| 1197 |
extern char *__progname; |
| 1198 |
|
| 1199 |
fprintf(stderr, |
| 1200 |
"usage: %s [-he] [-l log_level] [-f log_facility]\n", __progname); |
| 1201 |
exit(1); |
| 1202 |
} |
| 1203 |
|
| 1017 |
int |
1204 |
int |
| 1018 |
main(int ac, char **av) |
1205 |
main(int argc, char **argv) |
| 1019 |
{ |
1206 |
{ |
| 1020 |
fd_set *rset, *wset; |
1207 |
fd_set *rset, *wset; |
| 1021 |
int in, out, max; |
1208 |
int in, out, max, ch, skipargs = 0, log_stderr = 0; |
| 1022 |
ssize_t len, olen, set_size; |
1209 |
ssize_t len, olen, set_size; |
|
|
1210 |
SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; |
| 1211 |
char *cp, *chroot_path = NULL; |
| 1212 |
|
| 1213 |
extern int optind; |
| 1214 |
extern char *optarg; |
| 1215 |
extern char *__progname; |
| 1023 |
|
1216 |
|
| 1024 |
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ |
1217 |
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ |
| 1025 |
sanitise_stdfd(); |
1218 |
sanitise_stdfd(); |
| 1026 |
|
1219 |
|
| 1027 |
/* XXX should use getopt */ |
1220 |
log_init(__progname, log_level, log_facility, log_stderr); |
| 1028 |
|
1221 |
|
| 1029 |
handle_init(); |
1222 |
while (!skipargs && (ch = getopt(argc, argv, "C:f:l:che")) != -1) { |
|
|
1223 |
switch (ch) { |
| 1224 |
case 'C': |
| 1225 |
chroot_path = optarg; |
| 1226 |
break; |
| 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 |
} |
| 1030 |
|
1252 |
|
| 1031 |
#ifdef DEBUG_SFTP_SERVER |
1253 |
log_init(__progname, log_level, log_facility, log_stderr); |
| 1032 |
log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); |
1254 |
|
| 1033 |
#endif |
1255 |
if ((cp = getenv("SSH_CONNECTION")) != NULL) { |
|
|
1256 |
client_addr = xstrdup(cp); |
| 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 client %s local user %s", |
| 1269 |
client_addr, pw->pw_name); |
| 1270 |
|
| 1271 |
if (chroot_path != NULL) |
| 1272 |
do_chroot(chroot_path); |
| 1273 |
if (getuid() != geteuid()) |
| 1274 |
permanently_set_uid(pw); |
| 1275 |
|
| 1276 |
handle_init(); |
| 1034 |
|
1277 |
|
| 1035 |
in = dup(STDIN_FILENO); |
1278 |
in = dup(STDIN_FILENO); |
| 1036 |
out = dup(STDOUT_FILENO); |
1279 |
out = dup(STDOUT_FILENO); |
|
Lines 1060-1066
main(int ac, char **av)
Link Here
|
| 1060 |
if (select(max+1, rset, wset, NULL, NULL) < 0) { |
1303 |
if (select(max+1, rset, wset, NULL, NULL) < 0) { |
| 1061 |
if (errno == EINTR) |
1304 |
if (errno == EINTR) |
| 1062 |
continue; |
1305 |
continue; |
| 1063 |
exit(2); |
1306 |
error("select %s (client %s local user %s)", |
|
|
1307 |
strerror(errno), client_addr, pw->pw_name); |
| 1308 |
cleanup_exit(2); |
| 1064 |
} |
1309 |
} |
| 1065 |
|
1310 |
|
| 1066 |
/* copy stdin to iqueue */ |
1311 |
/* copy stdin to iqueue */ |
|
Lines 1069-1078
main(int ac, char **av)
Link Here
|
| 1069 |
len = read(in, buf, sizeof buf); |
1314 |
len = read(in, buf, sizeof buf); |
| 1070 |
if (len == 0) { |
1315 |
if (len == 0) { |
| 1071 |
debug("read eof"); |
1316 |
debug("read eof"); |
| 1072 |
exit(0); |
1317 |
logit("session closed for client %s " |
|
|
1318 |
"local user %s", client_addr, pw->pw_name); |
| 1319 |
cleanup_exit(0); |
| 1073 |
} else if (len < 0) { |
1320 |
} else if (len < 0) { |
| 1074 |
error("read error"); |
1321 |
error("read error %s (client %s local user %s)", |
| 1075 |
exit(1); |
1322 |
strerror(errno), client_addr, pw->pw_name); |
|
|
1323 |
cleanup_exit(1); |
| 1076 |
} else { |
1324 |
} else { |
| 1077 |
buffer_append(&iqueue, buf, len); |
1325 |
buffer_append(&iqueue, buf, len); |
| 1078 |
} |
1326 |
} |
|
Lines 1081-1088
main(int ac, char **av)
Link Here
|
| 1081 |
if (FD_ISSET(out, wset)) { |
1329 |
if (FD_ISSET(out, wset)) { |
| 1082 |
len = write(out, buffer_ptr(&oqueue), olen); |
1330 |
len = write(out, buffer_ptr(&oqueue), olen); |
| 1083 |
if (len < 0) { |
1331 |
if (len < 0) { |
| 1084 |
error("write error"); |
1332 |
error("write error %s (client %s local user %s)", |
| 1085 |
exit(1); |
1333 |
strerror(errno), client_addr, pw->pw_name); |
|
|
1334 |
cleanup_exit(1); |
| 1086 |
} else { |
1335 |
} else { |
| 1087 |
buffer_consume(&oqueue, len); |
1336 |
buffer_consume(&oqueue, len); |
| 1088 |
} |
1337 |
} |