- djm@cvs.openbsd.org 2006/07/06 10:47:57

[sftp-server.8 sftp-server.c]
     add commandline options to enable logging of transactions; ok markus@
This commit is contained in:
Damien Miller 2006-07-10 20:46:55 +10:00
parent 917f9b6b6e
commit fef95ad816
3 changed files with 295 additions and 58 deletions

View File

@ -38,6 +38,9 @@
- djm@cvs.openbsd.org 2006/07/06 10:47:05 - djm@cvs.openbsd.org 2006/07/06 10:47:05
[servconf.c servconf.h session.c sshd_config.5] [servconf.c servconf.h session.c sshd_config.5]
support arguments to Subsystem commands; ok markus@ support arguments to Subsystem commands; ok markus@
- djm@cvs.openbsd.org 2006/07/06 10:47:57
[sftp-server.8 sftp-server.c]
add commandline options to enable logging of transactions; ok markus@
20060706 20060706
- (dtucker) [configure.ac] Try AIX blibpath test in different order when - (dtucker) [configure.ac] Try AIX blibpath test in different order when
@ -4771,4 +4774,4 @@
- (djm) Trim deprecated options from INSTALL. Mention UsePAM - (djm) Trim deprecated options from INSTALL. Mention UsePAM
- (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu - (djm) Fix quote handling in sftp; Patch from admorten AT umich.edu
$Id: ChangeLog,v 1.4369 2006/07/10 10:36:47 djm Exp $ $Id: ChangeLog,v 1.4370 2006/07/10 10:46:55 djm Exp $

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: sftp-server.8,v 1.10 2003/10/08 08:27:36 jmc Exp $ .\" $OpenBSD: sftp-server.8,v 1.11 2006/07/06 10:47:57 djm Exp $
.\" .\"
.\" Copyright (c) 2000 Markus Friedl. All rights reserved. .\" Copyright (c) 2000 Markus Friedl. All rights reserved.
.\" .\"
@ -30,6 +30,8 @@
.Nd SFTP server subsystem .Nd SFTP server subsystem
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm sftp-server .Nm sftp-server
.Op Fl f Ar log_facility
.Op Fl l Ar log_level
.Sh DESCRIPTION .Sh DESCRIPTION
.Nm .Nm
is a program that speaks the server side of SFTP protocol is a program that speaks the server side of SFTP protocol
@ -40,9 +42,36 @@ is not intended to be called directly, but from
using the using the
.Cm Subsystem .Cm Subsystem
option. option.
.Pp
Command-line flags to
.Nm
should be specified in the
.Cm Subsystem
declaration.
See See
.Xr sshd_config 5 .Xr sshd_config 5
for more information. for more information.
.Pp
Valid options are:
.Bl -tag -width Ds
.It Fl f Ar log_facility
Specifies the facility code that is used when logging messages from
.Nm .
The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2,
LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7.
The default is AUTH.
.It Fl l Ar log_level
Specifies which messages will be logged by
.Nm .
The possible values are:
QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3.
INFO and VERBOSE log transactions that
.Nm
performs on behalf of the client.
DEBUG and DEBUG1 are equivalent.
DEBUG2 and DEBUG3 each specify higher levels of debugging output.
The default is ERROR.
.El
.Sh SEE ALSO .Sh SEE ALSO
.Xr sftp 1 , .Xr sftp 1 ,
.Xr ssh 1 , .Xr ssh 1 ,

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sftp-server.c,v 1.57 2006/03/30 09:58:16 djm Exp $ */ /* $OpenBSD: sftp-server.c,v 1.58 2006/07/06 10:47:57 djm Exp $ */
/* /*
* Copyright (c) 2000-2004 Markus Friedl. All rights reserved. * Copyright (c) 2000-2004 Markus Friedl. All rights reserved.
* *
@ -26,6 +26,7 @@
#include "log.h" #include "log.h"
#include "xmalloc.h" #include "xmalloc.h"
#include "misc.h" #include "misc.h"
#include "uidswap.h"
#include "sftp.h" #include "sftp.h"
#include "sftp-common.h" #include "sftp-common.h"
@ -34,9 +35,13 @@
#define get_int64() buffer_get_int64(&iqueue); #define get_int64() buffer_get_int64(&iqueue);
#define get_int() buffer_get_int(&iqueue); #define get_int() buffer_get_int(&iqueue);
#define get_string(lenp) buffer_get_string(&iqueue, lenp); #define get_string(lenp) buffer_get_string(&iqueue, lenp);
#define TRACE debug
extern char *__progname; /* Our verbosity */
LogLevel log_level = SYSLOG_LEVEL_ERROR;
/* Our client */
struct passwd *pw = NULL;
char *client_addr = NULL;
/* input and output queue */ /* input and output queue */
Buffer iqueue; Buffer iqueue;
@ -108,6 +113,33 @@ flags_from_portable(int pflags)
return flags; return flags;
} }
static const char *
string_from_portable(int pflags)
{
static char ret[128];
*ret = '\0';
#define PAPPEND(str) { \
if (*ret != '\0') \
strlcat(ret, ",", sizeof(ret)); \
strlcat(ret, str, sizeof(ret)); \
}
if (pflags & SSH2_FXF_READ)
PAPPEND("READ")
if (pflags & SSH2_FXF_WRITE)
PAPPEND("WRITE")
if (pflags & SSH2_FXF_CREAT)
PAPPEND("CREATE")
if (pflags & SSH2_FXF_TRUNC)
PAPPEND("TRUNCATE")
if (pflags & SSH2_FXF_EXCL)
PAPPEND("EXCL")
return ret;
}
static Attrib * static Attrib *
get_attrib(void) get_attrib(void)
{ {
@ -122,6 +154,7 @@ struct Handle {
DIR *dirp; DIR *dirp;
int fd; int fd;
char *name; char *name;
u_int64_t bytes_read, bytes_write;
}; };
enum { enum {
@ -152,6 +185,7 @@ handle_new(int use, const char *name, int fd, DIR *dirp)
handles[i].dirp = dirp; handles[i].dirp = dirp;
handles[i].fd = fd; handles[i].fd = fd;
handles[i].name = xstrdup(name); handles[i].name = xstrdup(name);
handles[i].bytes_read = handles[i].bytes_write = 0;
return i; return i;
} }
} }
@ -215,6 +249,36 @@ handle_to_fd(int handle)
return -1; return -1;
} }
static void
handle_update_read(int handle, ssize_t bytes)
{
if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0)
handles[handle].bytes_read += bytes;
}
static void
handle_update_write(int handle, ssize_t bytes)
{
if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0)
handles[handle].bytes_write += bytes;
}
static u_int64_t
handle_bytes_read(int handle)
{
if (handle_is_ok(handle, HANDLE_FILE))
return (handles[handle].bytes_read);
return 0;
}
static u_int64_t
handle_bytes_write(int handle)
{
if (handle_is_ok(handle, HANDLE_FILE))
return (handles[handle].bytes_write);
return 0;
}
static int static int
handle_close(int handle) handle_close(int handle)
{ {
@ -234,6 +298,31 @@ handle_close(int handle)
return ret; return ret;
} }
static void
handle_log_close(int handle, char *emsg)
{
if (handle_is_ok(handle, HANDLE_FILE)) {
logit("%s%sclose \"%s\" bytes read %llu written %llu",
emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ",
handle_to_name(handle),
handle_bytes_read(handle), handle_bytes_write(handle));
} else {
logit("%s%sclosedir \"%s\"",
emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ",
handle_to_name(handle));
}
}
static void
handle_log_exit(void)
{
u_int i;
for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
if (handles[i].use != HANDLE_UNUSED)
handle_log_close(i, "forced");
}
static int static int
get_handle(void) get_handle(void)
{ {
@ -260,10 +349,9 @@ send_msg(Buffer *m)
buffer_consume(m, mlen); buffer_consume(m, mlen);
} }
static void static const char *
send_status(u_int32_t id, u_int32_t status) status_to_message(u_int32_t status)
{ {
Buffer msg;
const char *status_messages[] = { const char *status_messages[] = {
"Success", /* SSH_FX_OK */ "Success", /* SSH_FX_OK */
"End of file", /* SSH_FX_EOF */ "End of file", /* SSH_FX_EOF */
@ -276,15 +364,24 @@ send_status(u_int32_t id, u_int32_t status)
"Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */
"Unknown error" /* Others */ "Unknown error" /* Others */
}; };
return (status_messages[MIN(status,SSH2_FX_MAX)]);
}
TRACE("sent status id %u error %u", id, status); static void
send_status(u_int32_t id, u_int32_t status)
{
Buffer msg;
debug3("request %u: sent status %u", id, status);
if (log_level > SYSLOG_LEVEL_VERBOSE ||
(status != SSH2_FX_OK && status != SSH2_FX_EOF))
logit("sent status %s", status_to_message(status));
buffer_init(&msg); buffer_init(&msg);
buffer_put_char(&msg, SSH2_FXP_STATUS); buffer_put_char(&msg, SSH2_FXP_STATUS);
buffer_put_int(&msg, id); buffer_put_int(&msg, id);
buffer_put_int(&msg, status); buffer_put_int(&msg, status);
if (version >= 3) { if (version >= 3) {
buffer_put_cstring(&msg, buffer_put_cstring(&msg, status_to_message(status));
status_messages[MIN(status,SSH2_FX_MAX)]);
buffer_put_cstring(&msg, ""); buffer_put_cstring(&msg, "");
} }
send_msg(&msg); send_msg(&msg);
@ -306,7 +403,7 @@ send_data_or_handle(char type, u_int32_t id, const char *data, int dlen)
static void static void
send_data(u_int32_t id, const char *data, int dlen) send_data(u_int32_t id, const char *data, int dlen)
{ {
TRACE("sent data id %u len %d", id, dlen); debug("request %u: sent data len %d", id, dlen);
send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
} }
@ -317,7 +414,7 @@ send_handle(u_int32_t id, int handle)
int hlen; int hlen;
handle_to_string(handle, &string, &hlen); handle_to_string(handle, &string, &hlen);
TRACE("sent handle id %u handle %d", id, handle); debug("request %u: sent handle handle %d", id, handle);
send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
xfree(string); xfree(string);
} }
@ -332,7 +429,7 @@ send_names(u_int32_t id, int count, const Stat *stats)
buffer_put_char(&msg, SSH2_FXP_NAME); buffer_put_char(&msg, SSH2_FXP_NAME);
buffer_put_int(&msg, id); buffer_put_int(&msg, id);
buffer_put_int(&msg, count); buffer_put_int(&msg, count);
TRACE("sent names id %u count %d", id, count); debug("request %u: sent names count %d", id, count);
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
buffer_put_cstring(&msg, stats[i].name); buffer_put_cstring(&msg, stats[i].name);
buffer_put_cstring(&msg, stats[i].long_name); buffer_put_cstring(&msg, stats[i].long_name);
@ -347,7 +444,7 @@ send_attrib(u_int32_t id, const Attrib *a)
{ {
Buffer msg; Buffer msg;
TRACE("sent attrib id %u have 0x%x", id, a->flags); debug("request %u: sent attrib have 0x%x", id, a->flags);
buffer_init(&msg); buffer_init(&msg);
buffer_put_char(&msg, SSH2_FXP_ATTRS); buffer_put_char(&msg, SSH2_FXP_ATTRS);
buffer_put_int(&msg, id); buffer_put_int(&msg, id);
@ -364,7 +461,7 @@ process_init(void)
Buffer msg; Buffer msg;
version = get_int(); version = get_int();
TRACE("client version %d", version); verbose("received client version %d", version);
buffer_init(&msg); buffer_init(&msg);
buffer_put_char(&msg, SSH2_FXP_VERSION); buffer_put_char(&msg, SSH2_FXP_VERSION);
buffer_put_int(&msg, SSH2_FILEXFER_VERSION); buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
@ -381,12 +478,14 @@ process_open(void)
int handle, fd, flags, mode, status = SSH2_FX_FAILURE; int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
id = get_int(); id = get_int();
debug3("request %u: open flags %d", id, pflags);
name = get_string(NULL); name = get_string(NULL);
pflags = get_int(); /* portable flags */ pflags = get_int(); /* portable flags */
a = get_attrib(); a = get_attrib();
flags = flags_from_portable(pflags); flags = flags_from_portable(pflags);
mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode); logit("open \"%s\" flags %s mode 0%o",
name, string_from_portable(pflags), mode);
fd = open(name, flags, mode); fd = open(name, flags, mode);
if (fd < 0) { if (fd < 0) {
status = errno_to_portable(errno); status = errno_to_portable(errno);
@ -412,7 +511,8 @@ process_close(void)
id = get_int(); id = get_int();
handle = get_handle(); handle = get_handle();
TRACE("close id %u handle %d", id, handle); debug3("request %u: close handle %u", id, handle);
handle_log_close(handle, NULL);
ret = handle_close(handle); ret = handle_close(handle);
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
send_status(id, status); send_status(id, status);
@ -431,11 +531,11 @@ process_read(void)
off = get_int64(); off = get_int64();
len = get_int(); len = get_int();
TRACE("read id %u handle %d off %llu len %d", id, handle, debug("request %u: read \"%s\" (handle %d) off %llu len %d",
(unsigned long long)off, len); id, handle_to_name(handle), handle, (unsigned long long)off, len);
if (len > sizeof buf) { if (len > sizeof buf) {
len = sizeof buf; len = sizeof buf;
logit("read change len %d", len); debug2("read change len %d", len);
} }
fd = handle_to_fd(handle); fd = handle_to_fd(handle);
if (fd >= 0) { if (fd >= 0) {
@ -451,6 +551,7 @@ process_read(void)
} else { } else {
send_data(id, buf, ret); send_data(id, buf, ret);
status = SSH2_FX_OK; status = SSH2_FX_OK;
handle_update_read(handle, ret);
} }
} }
} }
@ -472,8 +573,8 @@ process_write(void)
off = get_int64(); off = get_int64();
data = get_string(&len); data = get_string(&len);
TRACE("write id %u handle %d off %llu len %d", id, handle, debug("request %u: write \"%s\" (handle %d) off %llu len %d",
(unsigned long long)off, len); id, handle_to_name(handle), handle, (unsigned long long)off, len);
fd = handle_to_fd(handle); fd = handle_to_fd(handle);
if (fd >= 0) { if (fd >= 0) {
if (lseek(fd, off, SEEK_SET) < 0) { if (lseek(fd, off, SEEK_SET) < 0) {
@ -487,8 +588,9 @@ process_write(void)
status = errno_to_portable(errno); status = errno_to_portable(errno);
} else if ((size_t)ret == len) { } else if ((size_t)ret == len) {
status = SSH2_FX_OK; status = SSH2_FX_OK;
handle_update_write(handle, ret);
} else { } else {
logit("nothing at all written"); debug2("nothing at all written");
} }
} }
} }
@ -507,7 +609,8 @@ process_do_stat(int do_lstat)
id = get_int(); id = get_int();
name = get_string(NULL); name = get_string(NULL);
TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name); debug3("request %u: %sstat", id, do_lstat ? "l" : "");
verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name);
ret = do_lstat ? lstat(name, &st) : stat(name, &st); ret = do_lstat ? lstat(name, &st) : stat(name, &st);
if (ret < 0) { if (ret < 0) {
status = errno_to_portable(errno); status = errno_to_portable(errno);
@ -543,7 +646,8 @@ process_fstat(void)
id = get_int(); id = get_int();
handle = get_handle(); handle = get_handle();
TRACE("fstat id %u handle %d", id, handle); debug("request %u: fstat \"%s\" (handle %u)",
id, handle_to_name(handle), handle);
fd = handle_to_fd(handle); fd = handle_to_fd(handle);
if (fd >= 0) { if (fd >= 0) {
ret = fstat(fd, &st); ret = fstat(fd, &st);
@ -582,23 +686,33 @@ process_setstat(void)
id = get_int(); id = get_int();
name = get_string(NULL); name = get_string(NULL);
a = get_attrib(); a = get_attrib();
TRACE("setstat id %u name %s", id, name); debug("request %u: setstat name \"%s\"", id, name);
if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
logit("set \"%s\" size %llu", name, a->size);
ret = truncate(name, a->size); ret = truncate(name, a->size);
if (ret == -1) if (ret == -1)
status = errno_to_portable(errno); status = errno_to_portable(errno);
} }
if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
logit("set \"%s\" mode %04o", name, a->perm);
ret = chmod(name, a->perm & 0777); ret = chmod(name, a->perm & 0777);
if (ret == -1) if (ret == -1)
status = errno_to_portable(errno); status = errno_to_portable(errno);
} }
if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
char buf[64];
time_t t = a->mtime;
strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S",
localtime(&t));
logit("set \"%s\" modtime %s", name, buf);
ret = utimes(name, attrib_to_tv(a)); ret = utimes(name, attrib_to_tv(a));
if (ret == -1) if (ret == -1)
status = errno_to_portable(errno); status = errno_to_portable(errno);
} }
if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
logit("set \"%s\" owner %lu group %lu", name,
(u_long)a->uid, (u_long)a->gid);
ret = chown(name, a->uid, a->gid); ret = chown(name, a->uid, a->gid);
if (ret == -1) if (ret == -1)
status = errno_to_portable(errno); status = errno_to_portable(errno);
@ -614,23 +728,25 @@ process_fsetstat(void)
u_int32_t id; u_int32_t id;
int handle, fd, ret; int handle, fd, ret;
int status = SSH2_FX_OK; int status = SSH2_FX_OK;
char *name;
id = get_int(); id = get_int();
handle = get_handle(); handle = get_handle();
a = get_attrib(); a = get_attrib();
TRACE("fsetstat id %u handle %d", id, handle); debug("request %u: fsetstat handle %d", id, handle);
fd = handle_to_fd(handle); fd = handle_to_fd(handle);
name = handle_to_name(handle); if (fd < 0) {
if (fd < 0 || name == NULL) {
status = SSH2_FX_FAILURE; status = SSH2_FX_FAILURE;
} else { } else {
char *name = handle_to_name(handle);
if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
logit("set \"%s\" size %llu", name, a->size);
ret = ftruncate(fd, a->size); ret = ftruncate(fd, a->size);
if (ret == -1) if (ret == -1)
status = errno_to_portable(errno); status = errno_to_portable(errno);
} }
if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
logit("set \"%s\" mode %04o", name, a->perm);
#ifdef HAVE_FCHMOD #ifdef HAVE_FCHMOD
ret = fchmod(fd, a->perm & 0777); ret = fchmod(fd, a->perm & 0777);
#else #else
@ -640,6 +756,12 @@ process_fsetstat(void)
status = errno_to_portable(errno); status = errno_to_portable(errno);
} }
if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
char buf[64];
time_t t = a->mtime;
strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S",
localtime(&t));
logit("set \"%s\" modtime %s", name, buf);
#ifdef HAVE_FUTIMES #ifdef HAVE_FUTIMES
ret = futimes(fd, attrib_to_tv(a)); ret = futimes(fd, attrib_to_tv(a));
#else #else
@ -649,6 +771,8 @@ process_fsetstat(void)
status = errno_to_portable(errno); status = errno_to_portable(errno);
} }
if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
logit("set \"%s\" owner %lu group %lu", name,
(u_long)a->uid, (u_long)a->gid);
#ifdef HAVE_FCHOWN #ifdef HAVE_FCHOWN
ret = fchown(fd, a->uid, a->gid); ret = fchown(fd, a->uid, a->gid);
#else #else
@ -671,7 +795,8 @@ process_opendir(void)
id = get_int(); id = get_int();
path = get_string(NULL); path = get_string(NULL);
TRACE("opendir id %u path %s", id, path); debug3("request %u: opendir", id);
logit("opendir \"%s\"", path);
dirp = opendir(path); dirp = opendir(path);
if (dirp == NULL) { if (dirp == NULL) {
status = errno_to_portable(errno); status = errno_to_portable(errno);
@ -701,14 +826,15 @@ process_readdir(void)
id = get_int(); id = get_int();
handle = get_handle(); handle = get_handle();
TRACE("readdir id %u handle %d", id, handle); debug("request %u: readdir \"%s\" (handle %d)", id,
handle_to_name(handle), handle);
dirp = handle_to_dir(handle); dirp = handle_to_dir(handle);
path = handle_to_name(handle); path = handle_to_name(handle);
if (dirp == NULL || path == NULL) { if (dirp == NULL || path == NULL) {
send_status(id, SSH2_FX_FAILURE); send_status(id, SSH2_FX_FAILURE);
} else { } else {
struct stat st; struct stat st;
char pathname[1024]; char pathname[MAXPATHLEN];
Stat *stats; Stat *stats;
int nstats = 10, count = 0, i; int nstats = 10, count = 0, i;
@ -755,7 +881,8 @@ process_remove(void)
id = get_int(); id = get_int();
name = get_string(NULL); name = get_string(NULL);
TRACE("remove id %u name %s", id, name); debug3("request %u: remove", id);
logit("remove name \"%s\"", name);
ret = unlink(name); ret = unlink(name);
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
send_status(id, status); send_status(id, status);
@ -775,7 +902,8 @@ process_mkdir(void)
a = get_attrib(); a = get_attrib();
mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
a->perm & 0777 : 0777; a->perm & 0777 : 0777;
TRACE("mkdir id %u name %s mode 0%o", id, name, mode); debug3("request %u: mkdir", id);
logit("mkdir name \"%s\" mode 0%o", name, mode);
ret = mkdir(name, mode); ret = mkdir(name, mode);
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
send_status(id, status); send_status(id, status);
@ -791,7 +919,8 @@ process_rmdir(void)
id = get_int(); id = get_int();
name = get_string(NULL); name = get_string(NULL);
TRACE("rmdir id %u name %s", id, name); debug3("request %u: rmdir", id);
logit("rmdir name \"%s\"", name);
ret = rmdir(name); ret = rmdir(name);
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
send_status(id, status); send_status(id, status);
@ -811,7 +940,8 @@ process_realpath(void)
xfree(path); xfree(path);
path = xstrdup("."); path = xstrdup(".");
} }
TRACE("realpath id %u path %s", id, path); debug3("request %u: realpath", id);
verbose("realpath \"%s\"", path);
if (realpath(path, resolvedname) == NULL) { if (realpath(path, resolvedname) == NULL) {
send_status(id, errno_to_portable(errno)); send_status(id, errno_to_portable(errno));
} else { } else {
@ -834,7 +964,8 @@ process_rename(void)
id = get_int(); id = get_int();
oldpath = get_string(NULL); oldpath = get_string(NULL);
newpath = get_string(NULL); newpath = get_string(NULL);
TRACE("rename id %u old %s new %s", id, oldpath, newpath); debug3("request %u: rename", id);
logit("rename old \"%s\" new \"%s\"", oldpath, newpath);
status = SSH2_FX_FAILURE; status = SSH2_FX_FAILURE;
if (lstat(oldpath, &sb) == -1) if (lstat(oldpath, &sb) == -1)
status = errno_to_portable(errno); status = errno_to_portable(errno);
@ -889,7 +1020,8 @@ process_readlink(void)
id = get_int(); id = get_int();
path = get_string(NULL); path = get_string(NULL);
TRACE("readlink id %u path %s", id, path); debug3("request %u: readlink", id);
verbose("readlink \"%s\"", path);
if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1)
send_status(id, errno_to_portable(errno)); send_status(id, errno_to_portable(errno));
else { else {
@ -913,7 +1045,8 @@ process_symlink(void)
id = get_int(); id = get_int();
oldpath = get_string(NULL); oldpath = get_string(NULL);
newpath = get_string(NULL); newpath = get_string(NULL);
TRACE("symlink id %u old %s new %s", id, oldpath, newpath); debug3("request %u: symlink", id);
logit("symlink old \"%s\" new \"%s\"", oldpath, newpath);
/* this will fail if 'newpath' exists */ /* this will fail if 'newpath' exists */
ret = symlink(oldpath, newpath); ret = symlink(oldpath, newpath);
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
@ -951,8 +1084,9 @@ process(void)
cp = buffer_ptr(&iqueue); cp = buffer_ptr(&iqueue);
msg_len = get_u32(cp); msg_len = get_u32(cp);
if (msg_len > SFTP_MAX_MSG_LENGTH) { if (msg_len > SFTP_MAX_MSG_LENGTH) {
error("bad message "); error("bad message from %s local user %s",
exit(11); client_addr, pw->pw_name);
cleanup_exit(11);
} }
if (buf_len < msg_len + 4) if (buf_len < msg_len + 4)
return; return;
@ -1026,7 +1160,7 @@ process(void)
} }
/* discard the remaining bytes from the current packet */ /* discard the remaining bytes from the current packet */
if (buf_len < buffer_len(&iqueue)) if (buf_len < buffer_len(&iqueue))
fatal("iqueue grows"); fatal("iqueue grew unexpectedly");
consumed = buf_len - buffer_len(&iqueue); consumed = buf_len - buffer_len(&iqueue);
if (msg_len < consumed) if (msg_len < consumed)
fatal("msg_len %d < consumed %d", msg_len, consumed); fatal("msg_len %d < consumed %d", msg_len, consumed);
@ -1034,25 +1168,95 @@ process(void)
buffer_consume(&iqueue, msg_len - consumed); buffer_consume(&iqueue, msg_len - consumed);
} }
/* Cleanup handler that logs active handles upon normal exit */
void
cleanup_exit(int i)
{
if (pw != NULL && client_addr != NULL) {
handle_log_exit();
logit("session closed for local user %s from [%s]",
pw->pw_name, client_addr);
}
_exit(i);
}
static void
usage(void)
{
extern char *__progname;
fprintf(stderr,
"usage: %s [-he] [-l log_level] [-f log_facility]\n", __progname);
exit(1);
}
int int
main(int ac, char **av) main(int argc, char **argv)
{ {
fd_set *rset, *wset; fd_set *rset, *wset;
int in, out, max; int in, out, max, ch, skipargs = 0, log_stderr = 0;
ssize_t len, olen, set_size; ssize_t len, olen, set_size;
SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
char *cp;
extern int optind;
extern char *optarg;
extern char *__progname;
/* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
sanitise_stdfd(); sanitise_stdfd();
/* XXX should use getopt */ __progname = ssh_get_progname(argv[0]);
log_init(__progname, log_level, log_facility, log_stderr);
while (!skipargs && (ch = getopt(argc, argv, "C:f:l:che")) != -1) {
switch (ch) {
case 'c':
/*
* Ignore all arguments if we are invoked as a
* shell using "sftp-server -c command"
*/
skipargs = 1;
break;
case 'e':
log_stderr = 1;
break;
case 'l':
log_level = log_level_number(optarg);
if (log_level == SYSLOG_LEVEL_NOT_SET)
error("Invalid log level \"%s\"", optarg);
break;
case 'f':
log_facility = log_facility_number(optarg);
if (log_level == SYSLOG_FACILITY_NOT_SET)
error("Invalid log facility \"%s\"", optarg);
break;
case 'h':
default:
usage();
}
}
log_init(__progname, log_level, log_facility, log_stderr);
if ((cp = getenv("SSH_CONNECTION")) != NULL) {
client_addr = xstrdup(cp);
if ((cp = strchr(client_addr, ' ')) == NULL)
fatal("Malformed SSH_CONNECTION variable: \"%s\"",
getenv("SSH_CONNECTION"));
*cp = '\0';
} else
client_addr = xstrdup("UNKNOWN");
if ((pw = getpwuid(getuid())) == NULL)
fatal("No user found for uid %lu", (u_long)getuid());
pw = pwcopy(pw);
logit("session opened for local user %s from [%s]",
pw->pw_name, client_addr);
__progname = ssh_get_progname(av[0]);
handle_init(); handle_init();
#ifdef DEBUG_SFTP_SERVER
log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
#endif
in = dup(STDIN_FILENO); in = dup(STDIN_FILENO);
out = dup(STDOUT_FILENO); out = dup(STDOUT_FILENO);
@ -1086,7 +1290,8 @@ main(int ac, char **av)
if (select(max+1, rset, wset, NULL, NULL) < 0) { if (select(max+1, rset, wset, NULL, NULL) < 0) {
if (errno == EINTR) if (errno == EINTR)
continue; continue;
exit(2); error("select: %s", strerror(errno));
cleanup_exit(2);
} }
/* copy stdin to iqueue */ /* copy stdin to iqueue */
@ -1095,10 +1300,10 @@ main(int ac, char **av)
len = read(in, buf, sizeof buf); len = read(in, buf, sizeof buf);
if (len == 0) { if (len == 0) {
debug("read eof"); debug("read eof");
exit(0); cleanup_exit(0);
} else if (len < 0) { } else if (len < 0) {
error("read error"); error("read: %s", strerror(errno));
exit(1); cleanup_exit(1);
} else { } else {
buffer_append(&iqueue, buf, len); buffer_append(&iqueue, buf, len);
} }
@ -1107,8 +1312,8 @@ main(int ac, char **av)
if (FD_ISSET(out, wset)) { if (FD_ISSET(out, wset)) {
len = write(out, buffer_ptr(&oqueue), olen); len = write(out, buffer_ptr(&oqueue), olen);
if (len < 0) { if (len < 0) {
error("write error"); error("write: %s", strerror(errno));
exit(1); cleanup_exit(1);
} else { } else {
buffer_consume(&oqueue, len); buffer_consume(&oqueue, len);
} }