version 1.57, 2006/03/30 09:58:16 |
version 1.58, 2006/07/06 10:47:57 |
|
|
#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" |
|
|
#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 |
|
|
|
|
/* 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; |
Buffer oqueue; |
Buffer oqueue; |
|
|
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) |
{ |
{ |
|
|
DIR *dirp; |
DIR *dirp; |
int fd; |
int fd; |
char *name; |
char *name; |
|
u_int64_t bytes_read, bytes_write; |
}; |
}; |
|
|
enum { |
enum { |
|
|
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; |
} |
} |
} |
} |
|
|
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) |
{ |
{ |
|
|
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) |
{ |
{ |
|
|
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 */ |
|
|
"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); |
|
|
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); |
} |
} |
|
|
|
|
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); |
} |
} |
|
|
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); |
|
|
{ |
{ |
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); |
|
|
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); |
|
|
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); |
|
|
|
|
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); |
|
|
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) { |
|
|
} 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); |
} |
} |
} |
} |
} |
} |
|
|
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) { |
|
|
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"); |
} |
} |
} |
} |
} |
} |
|
|
|
|
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); |
|
|
|
|
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); |
|
|
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); |
|
|
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); |
if (fd < 0) { |
if (fd < 0) { |
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); |
ret = fchmod(fd, a->perm & 0777); |
ret = fchmod(fd, 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 = futimes(fd, attrib_to_tv(a)); |
ret = futimes(fd, 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 = fchown(fd, a->uid, a->gid); |
ret = fchown(fd, a->uid, a->gid); |
if (ret == -1) |
if (ret == -1) |
status = errno_to_portable(errno); |
status = errno_to_portable(errno); |
|
|
|
|
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); |
|
|
|
|
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; |
|
|
|
|
|
|
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); |
|
|
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); |
|
|
|
|
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); |
|
|
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 { |
|
|
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); |
|
|
|
|
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 { |
|
|
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; |
|
|
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; |
|
|
} |
} |
/* 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); |
|
|
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 */ |
log_init(__progname, log_level, log_facility, log_stderr); |
|
|
handle_init(); |
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(); |
|
} |
|
} |
|
|
#ifdef DEBUG_SFTP_SERVER |
log_init(__progname, log_level, log_facility, log_stderr); |
log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0); |
|
#endif |
|
|
|
|
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); |
|
|
|
handle_init(); |
|
|
in = dup(STDIN_FILENO); |
in = dup(STDIN_FILENO); |
out = dup(STDOUT_FILENO); |
out = dup(STDOUT_FILENO); |
|
|
|
|
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 */ |
|
|
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); |
} |
} |
|
|
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); |
} |
} |