version 1.97, 2013/05/17 00:13:14 |
version 1.98, 2013/10/09 23:42:17 |
|
|
#include "buffer.h" |
#include "buffer.h" |
#include "log.h" |
#include "log.h" |
#include "misc.h" |
#include "misc.h" |
|
#include "match.h" |
#include "uidswap.h" |
#include "uidswap.h" |
|
|
#include "sftp.h" |
#include "sftp.h" |
|
|
#define get_string(lenp) buffer_get_string(&iqueue, lenp); |
#define get_string(lenp) buffer_get_string(&iqueue, lenp); |
|
|
/* Our verbosity */ |
/* Our verbosity */ |
LogLevel log_level = SYSLOG_LEVEL_ERROR; |
static LogLevel log_level = SYSLOG_LEVEL_ERROR; |
|
|
/* Our client */ |
/* Our client */ |
struct passwd *pw = NULL; |
static struct passwd *pw = NULL; |
char *client_addr = NULL; |
static char *client_addr = NULL; |
|
|
/* input and output queue */ |
/* input and output queue */ |
Buffer iqueue; |
static Buffer iqueue; |
Buffer oqueue; |
static Buffer oqueue; |
|
|
/* Version of client */ |
/* Version of client */ |
u_int version; |
static u_int version; |
|
|
|
/* SSH2_FXP_INIT received */ |
|
static int init_done; |
|
|
/* Disable writes */ |
/* Disable writes */ |
int readonly; |
static int readonly; |
|
|
/* portable attributes, etc. */ |
/* Requests that are allowed/denied */ |
|
static char *request_whitelist, *request_blacklist; |
|
|
|
/* portable attributes, etc. */ |
typedef struct Stat Stat; |
typedef struct Stat Stat; |
|
|
struct Stat { |
struct Stat { |
|
|
Attrib attrib; |
Attrib attrib; |
}; |
}; |
|
|
|
/* Packet handlers */ |
|
static void process_open(u_int32_t id); |
|
static void process_close(u_int32_t id); |
|
static void process_read(u_int32_t id); |
|
static void process_write(u_int32_t id); |
|
static void process_stat(u_int32_t id); |
|
static void process_lstat(u_int32_t id); |
|
static void process_fstat(u_int32_t id); |
|
static void process_setstat(u_int32_t id); |
|
static void process_fsetstat(u_int32_t id); |
|
static void process_opendir(u_int32_t id); |
|
static void process_readdir(u_int32_t id); |
|
static void process_remove(u_int32_t id); |
|
static void process_mkdir(u_int32_t id); |
|
static void process_rmdir(u_int32_t id); |
|
static void process_realpath(u_int32_t id); |
|
static void process_rename(u_int32_t id); |
|
static void process_readlink(u_int32_t id); |
|
static void process_symlink(u_int32_t id); |
|
static void process_extended_posix_rename(u_int32_t id); |
|
static void process_extended_statvfs(u_int32_t id); |
|
static void process_extended_fstatvfs(u_int32_t id); |
|
static void process_extended_hardlink(u_int32_t id); |
|
static void process_extended(u_int32_t id); |
|
|
|
struct sftp_handler { |
|
const char *name; /* user-visible name for fine-grained perms */ |
|
const char *ext_name; /* extended request name */ |
|
u_int type; /* packet type, for non extended packets */ |
|
void (*handler)(u_int32_t); |
|
int does_write; /* if nonzero, banned for readonly mode */ |
|
}; |
|
|
|
struct sftp_handler handlers[] = { |
|
/* NB. SSH2_FXP_OPEN does the readonly check in the handler itself */ |
|
{ "open", NULL, SSH2_FXP_OPEN, process_open, 0 }, |
|
{ "close", NULL, SSH2_FXP_CLOSE, process_close, 0 }, |
|
{ "read", NULL, SSH2_FXP_READ, process_read, 0 }, |
|
{ "write", NULL, SSH2_FXP_WRITE, process_write, 1 }, |
|
{ "lstat", NULL, SSH2_FXP_LSTAT, process_lstat, 0 }, |
|
{ "fstat", NULL, SSH2_FXP_FSTAT, process_fstat, 0 }, |
|
{ "setstat", NULL, SSH2_FXP_SETSTAT, process_setstat, 1 }, |
|
{ "fsetstat", NULL, SSH2_FXP_FSETSTAT, process_fsetstat, 1 }, |
|
{ "opendir", NULL, SSH2_FXP_OPENDIR, process_opendir, 0 }, |
|
{ "readdir", NULL, SSH2_FXP_READDIR, process_readdir, 0 }, |
|
{ "remove", NULL, SSH2_FXP_REMOVE, process_remove, 1 }, |
|
{ "mkdir", NULL, SSH2_FXP_MKDIR, process_mkdir, 1 }, |
|
{ "rmdir", NULL, SSH2_FXP_RMDIR, process_rmdir, 1 }, |
|
{ "realpath", NULL, SSH2_FXP_REALPATH, process_realpath, 0 }, |
|
{ "stat", NULL, SSH2_FXP_STAT, process_stat, 0 }, |
|
{ "rename", NULL, SSH2_FXP_RENAME, process_rename, 1 }, |
|
{ "readlink", NULL, SSH2_FXP_READLINK, process_readlink, 0 }, |
|
{ "symlink", NULL, SSH2_FXP_SYMLINK, process_symlink, 1 }, |
|
{ NULL, NULL, 0, NULL, 0 } |
|
}; |
|
|
|
/* SSH2_FXP_EXTENDED submessages */ |
|
struct sftp_handler extended_handlers[] = { |
|
{ "posix-rename", "posix-rename@openssh.com", 0, |
|
process_extended_posix_rename, 1 }, |
|
{ "statvfs", "statvfs@openssh.com", 0, process_extended_statvfs, 0 }, |
|
{ "fstatvfs", "fstatvfs@openssh.com", 0, process_extended_fstatvfs, 0 }, |
|
{ "hardlink", "hardlink@openssh.com", 0, process_extended_hardlink, 1 }, |
|
{ NULL, NULL, 0, NULL, 0 } |
|
}; |
|
|
static int |
static int |
|
request_permitted(struct sftp_handler *h) |
|
{ |
|
char *result; |
|
|
|
if (readonly && h->does_write) { |
|
verbose("Refusing %s request in read-only mode", h->name); |
|
return 0; |
|
} |
|
if (request_blacklist != NULL && |
|
((result = match_list(h->name, request_blacklist, NULL))) != NULL) { |
|
free(result); |
|
verbose("Refusing blacklisted %s request", h->name); |
|
return 0; |
|
} |
|
if (request_whitelist != NULL && |
|
((result = match_list(h->name, request_whitelist, NULL))) != NULL) { |
|
free(result); |
|
debug2("Permitting whitelisted %s request", h->name); |
|
return 1; |
|
} |
|
if (request_whitelist != NULL) { |
|
verbose("Refusing non-whitelisted %s request", h->name); |
|
return 0; |
|
} |
|
return 1; |
|
} |
|
|
|
static int |
errno_to_portable(int unixerrno) |
errno_to_portable(int unixerrno) |
{ |
{ |
int ret = 0; |
int ret = 0; |
|
|
} |
} |
|
|
static void |
static void |
process_open(void) |
process_open(u_int32_t id) |
{ |
{ |
u_int32_t id, pflags; |
u_int32_t pflags; |
Attrib *a; |
Attrib *a; |
char *name; |
char *name; |
int handle, fd, flags, mode, status = SSH2_FX_FAILURE; |
int handle, fd, flags, mode, status = SSH2_FX_FAILURE; |
|
|
id = get_int(); |
|
name = get_string(NULL); |
name = get_string(NULL); |
pflags = get_int(); /* portable flags */ |
pflags = get_int(); /* portable flags */ |
debug3("request %u: open flags %d", id, pflags); |
debug3("request %u: open flags %d", id, pflags); |
|
|
logit("open \"%s\" flags %s mode 0%o", |
logit("open \"%s\" flags %s mode 0%o", |
name, string_from_portable(pflags), mode); |
name, string_from_portable(pflags), mode); |
if (readonly && |
if (readonly && |
((flags & O_ACCMODE) == O_WRONLY || (flags & O_ACCMODE) == O_RDWR)) |
((flags & O_ACCMODE) == O_WRONLY || |
status = SSH2_FX_PERMISSION_DENIED; |
(flags & O_ACCMODE) == O_RDWR)) { |
else { |
verbose("Refusing open request in read-only mode"); |
|
status = SSH2_FX_PERMISSION_DENIED; |
|
} else { |
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); |
|
|
} |
} |
|
|
static void |
static void |
process_close(void) |
process_close(u_int32_t id) |
{ |
{ |
u_int32_t id; |
|
int handle, ret, status = SSH2_FX_FAILURE; |
int handle, ret, status = SSH2_FX_FAILURE; |
|
|
id = get_int(); |
|
handle = get_handle(); |
handle = get_handle(); |
debug3("request %u: close handle %u", id, handle); |
debug3("request %u: close handle %u", id, handle); |
handle_log_close(handle, NULL); |
handle_log_close(handle, NULL); |
|
|
} |
} |
|
|
static void |
static void |
process_read(void) |
process_read(u_int32_t id) |
{ |
{ |
char buf[64*1024]; |
char buf[64*1024]; |
u_int32_t id, len; |
u_int32_t len; |
int handle, fd, ret, status = SSH2_FX_FAILURE; |
int handle, fd, ret, status = SSH2_FX_FAILURE; |
u_int64_t off; |
u_int64_t off; |
|
|
id = get_int(); |
|
handle = get_handle(); |
handle = get_handle(); |
off = get_int64(); |
off = get_int64(); |
len = get_int(); |
len = get_int(); |
|
|
} |
} |
|
|
static void |
static void |
process_write(void) |
process_write(u_int32_t id) |
{ |
{ |
u_int32_t id; |
|
u_int64_t off; |
u_int64_t off; |
u_int len; |
u_int len; |
int handle, fd, ret, status; |
int handle, fd, ret, status; |
char *data; |
char *data; |
|
|
id = get_int(); |
|
handle = get_handle(); |
handle = get_handle(); |
off = get_int64(); |
off = get_int64(); |
data = get_string(&len); |
data = get_string(&len); |
|
|
|
|
if (fd < 0) |
if (fd < 0) |
status = SSH2_FX_FAILURE; |
status = SSH2_FX_FAILURE; |
else if (readonly) |
|
status = SSH2_FX_PERMISSION_DENIED; |
|
else { |
else { |
if (lseek(fd, off, SEEK_SET) < 0) { |
if (lseek(fd, off, SEEK_SET) < 0) { |
status = errno_to_portable(errno); |
status = errno_to_portable(errno); |
|
|
} |
} |
|
|
static void |
static void |
process_do_stat(int do_lstat) |
process_do_stat(u_int32_t id, int do_lstat) |
{ |
{ |
Attrib a; |
Attrib a; |
struct stat st; |
struct stat st; |
u_int32_t id; |
|
char *name; |
char *name; |
int ret, status = SSH2_FX_FAILURE; |
int ret, status = SSH2_FX_FAILURE; |
|
|
id = get_int(); |
|
name = get_string(NULL); |
name = get_string(NULL); |
debug3("request %u: %sstat", id, do_lstat ? "l" : ""); |
debug3("request %u: %sstat", id, do_lstat ? "l" : ""); |
verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); |
verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); |
|
|
} |
} |
|
|
static void |
static void |
process_stat(void) |
process_stat(u_int32_t id) |
{ |
{ |
process_do_stat(0); |
process_do_stat(id, 0); |
} |
} |
|
|
static void |
static void |
process_lstat(void) |
process_lstat(u_int32_t id) |
{ |
{ |
process_do_stat(1); |
process_do_stat(id, 1); |
} |
} |
|
|
static void |
static void |
process_fstat(void) |
process_fstat(u_int32_t id) |
{ |
{ |
Attrib a; |
Attrib a; |
struct stat st; |
struct stat st; |
u_int32_t id; |
|
int fd, ret, handle, status = SSH2_FX_FAILURE; |
int fd, ret, handle, status = SSH2_FX_FAILURE; |
|
|
id = get_int(); |
|
handle = get_handle(); |
handle = get_handle(); |
debug("request %u: fstat \"%s\" (handle %u)", |
debug("request %u: fstat \"%s\" (handle %u)", |
id, handle_to_name(handle), handle); |
id, handle_to_name(handle), handle); |
|
|
} |
} |
|
|
static void |
static void |
process_setstat(void) |
process_setstat(u_int32_t id) |
{ |
{ |
Attrib *a; |
Attrib *a; |
u_int32_t id; |
|
char *name; |
char *name; |
int status = SSH2_FX_OK, ret; |
int status = SSH2_FX_OK, ret; |
|
|
id = get_int(); |
|
name = get_string(NULL); |
name = get_string(NULL); |
a = get_attrib(); |
a = get_attrib(); |
debug("request %u: setstat name \"%s\"", id, name); |
debug("request %u: setstat name \"%s\"", id, name); |
if (readonly) { |
|
status = SSH2_FX_PERMISSION_DENIED; |
|
a->flags = 0; |
|
} |
|
if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { |
if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { |
logit("set \"%s\" size %llu", |
logit("set \"%s\" size %llu", |
name, (unsigned long long)a->size); |
name, (unsigned long long)a->size); |
|
|
} |
} |
|
|
static void |
static void |
process_fsetstat(void) |
process_fsetstat(u_int32_t id) |
{ |
{ |
Attrib *a; |
Attrib *a; |
u_int32_t id; |
|
int handle, fd, ret; |
int handle, fd, ret; |
int status = SSH2_FX_OK; |
int status = SSH2_FX_OK; |
|
|
id = get_int(); |
|
handle = get_handle(); |
handle = get_handle(); |
a = get_attrib(); |
a = get_attrib(); |
debug("request %u: fsetstat 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 if (readonly) |
|
status = SSH2_FX_PERMISSION_DENIED; |
|
else { |
else { |
char *name = handle_to_name(handle); |
char *name = handle_to_name(handle); |
|
|
|
|
} |
} |
|
|
static void |
static void |
process_opendir(void) |
process_opendir(u_int32_t id) |
{ |
{ |
DIR *dirp = NULL; |
DIR *dirp = NULL; |
char *path; |
char *path; |
int handle, status = SSH2_FX_FAILURE; |
int handle, status = SSH2_FX_FAILURE; |
u_int32_t id; |
|
|
|
id = get_int(); |
|
path = get_string(NULL); |
path = get_string(NULL); |
debug3("request %u: opendir", id); |
debug3("request %u: opendir", id); |
logit("opendir \"%s\"", path); |
logit("opendir \"%s\"", path); |
|
|
} |
} |
|
|
static void |
static void |
process_readdir(void) |
process_readdir(u_int32_t id) |
{ |
{ |
DIR *dirp; |
DIR *dirp; |
struct dirent *dp; |
struct dirent *dp; |
char *path; |
char *path; |
int handle; |
int handle; |
u_int32_t id; |
|
|
|
id = get_int(); |
|
handle = get_handle(); |
handle = get_handle(); |
debug("request %u: readdir \"%s\" (handle %d)", id, |
debug("request %u: readdir \"%s\" (handle %d)", id, |
handle_to_name(handle), handle); |
handle_to_name(handle), handle); |
|
|
} |
} |
|
|
static void |
static void |
process_remove(void) |
process_remove(u_int32_t id) |
{ |
{ |
char *name; |
char *name; |
u_int32_t id; |
|
int status = SSH2_FX_FAILURE; |
int status = SSH2_FX_FAILURE; |
int ret; |
int ret; |
|
|
id = get_int(); |
|
name = get_string(NULL); |
name = get_string(NULL); |
debug3("request %u: remove", id); |
debug3("request %u: remove", id); |
logit("remove name \"%s\"", name); |
logit("remove name \"%s\"", name); |
if (readonly) |
ret = unlink(name); |
status = SSH2_FX_PERMISSION_DENIED; |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
else { |
|
ret = unlink(name); |
|
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
|
} |
|
send_status(id, status); |
send_status(id, status); |
free(name); |
free(name); |
} |
} |
|
|
static void |
static void |
process_mkdir(void) |
process_mkdir(u_int32_t id) |
{ |
{ |
Attrib *a; |
Attrib *a; |
u_int32_t id; |
|
char *name; |
char *name; |
int ret, mode, status = SSH2_FX_FAILURE; |
int ret, mode, status = SSH2_FX_FAILURE; |
|
|
id = get_int(); |
|
name = get_string(NULL); |
name = get_string(NULL); |
a = get_attrib(); |
a = get_attrib(); |
mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? |
mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? |
a->perm & 07777 : 0777; |
a->perm & 07777 : 0777; |
debug3("request %u: mkdir", id); |
debug3("request %u: mkdir", id); |
logit("mkdir name \"%s\" mode 0%o", name, mode); |
logit("mkdir name \"%s\" mode 0%o", name, mode); |
if (readonly) |
ret = mkdir(name, mode); |
status = SSH2_FX_PERMISSION_DENIED; |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
else { |
|
ret = mkdir(name, mode); |
|
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
|
} |
|
send_status(id, status); |
send_status(id, status); |
free(name); |
free(name); |
} |
} |
|
|
static void |
static void |
process_rmdir(void) |
process_rmdir(u_int32_t id) |
{ |
{ |
u_int32_t id; |
|
char *name; |
char *name; |
int ret, status; |
int ret, status; |
|
|
id = get_int(); |
|
name = get_string(NULL); |
name = get_string(NULL); |
debug3("request %u: rmdir", id); |
debug3("request %u: rmdir", id); |
logit("rmdir name \"%s\"", name); |
logit("rmdir name \"%s\"", name); |
if (readonly) |
ret = rmdir(name); |
status = SSH2_FX_PERMISSION_DENIED; |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
else { |
|
ret = rmdir(name); |
|
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
|
} |
|
send_status(id, status); |
send_status(id, status); |
free(name); |
free(name); |
} |
} |
|
|
static void |
static void |
process_realpath(void) |
process_realpath(u_int32_t id) |
{ |
{ |
char resolvedname[MAXPATHLEN]; |
char resolvedname[MAXPATHLEN]; |
u_int32_t id; |
|
char *path; |
char *path; |
|
|
id = get_int(); |
|
path = get_string(NULL); |
path = get_string(NULL); |
if (path[0] == '\0') { |
if (path[0] == '\0') { |
free(path); |
free(path); |
|
|
} |
} |
|
|
static void |
static void |
process_rename(void) |
process_rename(u_int32_t id) |
{ |
{ |
u_int32_t id; |
|
char *oldpath, *newpath; |
char *oldpath, *newpath; |
int status; |
int status; |
struct stat sb; |
struct stat sb; |
|
|
id = get_int(); |
|
oldpath = get_string(NULL); |
oldpath = get_string(NULL); |
newpath = get_string(NULL); |
newpath = get_string(NULL); |
debug3("request %u: rename", id); |
debug3("request %u: rename", id); |
logit("rename old \"%s\" new \"%s\"", oldpath, newpath); |
logit("rename old \"%s\" new \"%s\"", oldpath, newpath); |
status = SSH2_FX_FAILURE; |
status = SSH2_FX_FAILURE; |
if (readonly) |
if (lstat(oldpath, &sb) == -1) |
status = SSH2_FX_PERMISSION_DENIED; |
|
else if (lstat(oldpath, &sb) == -1) |
|
status = errno_to_portable(errno); |
status = errno_to_portable(errno); |
else if (S_ISREG(sb.st_mode)) { |
else if (S_ISREG(sb.st_mode)) { |
/* Race-free rename of regular files */ |
/* Race-free rename of regular files */ |
|
|
} |
} |
|
|
static void |
static void |
process_readlink(void) |
process_readlink(u_int32_t id) |
{ |
{ |
u_int32_t id; |
|
int len; |
int len; |
char buf[MAXPATHLEN]; |
char buf[MAXPATHLEN]; |
char *path; |
char *path; |
|
|
id = get_int(); |
|
path = get_string(NULL); |
path = get_string(NULL); |
debug3("request %u: readlink", id); |
debug3("request %u: readlink", id); |
verbose("readlink \"%s\"", path); |
verbose("readlink \"%s\"", path); |
|
|
} |
} |
|
|
static void |
static void |
process_symlink(void) |
process_symlink(u_int32_t id) |
{ |
{ |
u_int32_t id; |
|
char *oldpath, *newpath; |
char *oldpath, *newpath; |
int ret, status; |
int ret, status; |
|
|
id = get_int(); |
|
oldpath = get_string(NULL); |
oldpath = get_string(NULL); |
newpath = get_string(NULL); |
newpath = get_string(NULL); |
debug3("request %u: symlink", id); |
debug3("request %u: symlink", id); |
logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); |
logit("symlink old \"%s\" new \"%s\"", oldpath, newpath); |
/* this will fail if 'newpath' exists */ |
/* this will fail if 'newpath' exists */ |
if (readonly) |
ret = symlink(oldpath, newpath); |
status = SSH2_FX_PERMISSION_DENIED; |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
else { |
|
ret = symlink(oldpath, newpath); |
|
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
|
} |
|
send_status(id, status); |
send_status(id, status); |
free(oldpath); |
free(oldpath); |
free(newpath); |
free(newpath); |
|
|
newpath = get_string(NULL); |
newpath = get_string(NULL); |
debug3("request %u: posix-rename", id); |
debug3("request %u: posix-rename", id); |
logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); |
logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath); |
if (readonly) |
ret = rename(oldpath, newpath); |
status = SSH2_FX_PERMISSION_DENIED; |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
else { |
|
ret = rename(oldpath, newpath); |
|
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
|
} |
|
send_status(id, status); |
send_status(id, status); |
free(oldpath); |
free(oldpath); |
free(newpath); |
free(newpath); |
|
|
newpath = get_string(NULL); |
newpath = get_string(NULL); |
debug3("request %u: hardlink", id); |
debug3("request %u: hardlink", id); |
logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath); |
logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath); |
if (readonly) |
ret = link(oldpath, newpath); |
status = SSH2_FX_PERMISSION_DENIED; |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
else { |
|
ret = link(oldpath, newpath); |
|
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
|
} |
|
send_status(id, status); |
send_status(id, status); |
free(oldpath); |
free(oldpath); |
free(newpath); |
free(newpath); |
} |
} |
|
|
static void |
static void |
process_extended(void) |
process_extended(u_int32_t id) |
{ |
{ |
u_int32_t id; |
|
char *request; |
char *request; |
|
u_int i; |
|
|
id = get_int(); |
|
request = get_string(NULL); |
request = get_string(NULL); |
if (strcmp(request, "posix-rename@openssh.com") == 0) |
for (i = 0; extended_handlers[i].handler != NULL; i++) { |
process_extended_posix_rename(id); |
if (strcmp(request, extended_handlers[i].ext_name) == 0) { |
else if (strcmp(request, "statvfs@openssh.com") == 0) |
if (!request_permitted(&extended_handlers[i])) |
process_extended_statvfs(id); |
send_status(id, SSH2_FX_PERMISSION_DENIED); |
else if (strcmp(request, "fstatvfs@openssh.com") == 0) |
else |
process_extended_fstatvfs(id); |
extended_handlers[i].handler(id); |
else if (strcmp(request, "hardlink@openssh.com") == 0) |
break; |
process_extended_hardlink(id); |
} |
else |
} |
|
if (extended_handlers[i].handler == NULL) { |
|
error("Unknown extended request \"%.100s\"", request); |
send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ |
send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ |
|
} |
free(request); |
free(request); |
} |
} |
|
|
|
|
static void |
static void |
process(void) |
process(void) |
{ |
{ |
u_int msg_len; |
u_int msg_len, buf_len, consumed, type, i; |
u_int buf_len; |
|
u_int consumed; |
|
u_int type; |
|
u_char *cp; |
u_char *cp; |
|
u_int32_t id; |
|
|
buf_len = buffer_len(&iqueue); |
buf_len = buffer_len(&iqueue); |
if (buf_len < 5) |
if (buf_len < 5) |
|
|
buffer_consume(&iqueue, 4); |
buffer_consume(&iqueue, 4); |
buf_len -= 4; |
buf_len -= 4; |
type = buffer_get_char(&iqueue); |
type = buffer_get_char(&iqueue); |
|
|
switch (type) { |
switch (type) { |
case SSH2_FXP_INIT: |
case SSH2_FXP_INIT: |
process_init(); |
process_init(); |
|
init_done = 1; |
break; |
break; |
case SSH2_FXP_OPEN: |
|
process_open(); |
|
break; |
|
case SSH2_FXP_CLOSE: |
|
process_close(); |
|
break; |
|
case SSH2_FXP_READ: |
|
process_read(); |
|
break; |
|
case SSH2_FXP_WRITE: |
|
process_write(); |
|
break; |
|
case SSH2_FXP_LSTAT: |
|
process_lstat(); |
|
break; |
|
case SSH2_FXP_FSTAT: |
|
process_fstat(); |
|
break; |
|
case SSH2_FXP_SETSTAT: |
|
process_setstat(); |
|
break; |
|
case SSH2_FXP_FSETSTAT: |
|
process_fsetstat(); |
|
break; |
|
case SSH2_FXP_OPENDIR: |
|
process_opendir(); |
|
break; |
|
case SSH2_FXP_READDIR: |
|
process_readdir(); |
|
break; |
|
case SSH2_FXP_REMOVE: |
|
process_remove(); |
|
break; |
|
case SSH2_FXP_MKDIR: |
|
process_mkdir(); |
|
break; |
|
case SSH2_FXP_RMDIR: |
|
process_rmdir(); |
|
break; |
|
case SSH2_FXP_REALPATH: |
|
process_realpath(); |
|
break; |
|
case SSH2_FXP_STAT: |
|
process_stat(); |
|
break; |
|
case SSH2_FXP_RENAME: |
|
process_rename(); |
|
break; |
|
case SSH2_FXP_READLINK: |
|
process_readlink(); |
|
break; |
|
case SSH2_FXP_SYMLINK: |
|
process_symlink(); |
|
break; |
|
case SSH2_FXP_EXTENDED: |
case SSH2_FXP_EXTENDED: |
process_extended(); |
if (!init_done) |
|
fatal("Received extended request before init"); |
|
id = get_int(); |
|
process_extended(id); |
break; |
break; |
default: |
default: |
error("Unknown message %d", type); |
if (!init_done) |
break; |
fatal("Received %u request before init", type); |
|
id = get_int(); |
|
for (i = 0; handlers[i].handler != NULL; i++) { |
|
if (type == handlers[i].type) { |
|
if (!request_permitted(&handlers[i])) { |
|
send_status(id, |
|
SSH2_FX_PERMISSION_DENIED); |
|
} else { |
|
handlers[i].handler(id); |
|
} |
|
break; |
|
} |
|
} |
|
if (handlers[i].handler == NULL) |
|
error("Unknown message %u", type); |
} |
} |
/* 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)) { |
|
|
} |
} |
consumed = buf_len - buffer_len(&iqueue); |
consumed = buf_len - buffer_len(&iqueue); |
if (msg_len < consumed) { |
if (msg_len < consumed) { |
error("msg_len %d < consumed %d", msg_len, consumed); |
error("msg_len %u < consumed %u", msg_len, consumed); |
sftp_server_cleanup_exit(255); |
sftp_server_cleanup_exit(255); |
} |
} |
if (msg_len > consumed) |
if (msg_len > consumed) |
|
|
sftp_server_main(int argc, char **argv, struct passwd *user_pw) |
sftp_server_main(int argc, char **argv, struct passwd *user_pw) |
{ |
{ |
fd_set *rset, *wset; |
fd_set *rset, *wset; |
int in, out, max, ch, skipargs = 0, log_stderr = 0; |
int i, 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; |
SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; |
char *cp, *homedir = NULL, buf[4*4096]; |
char *cp, *homedir = NULL, buf[4*4096]; |
|
|
|
|
pw = pwcopy(user_pw); |
pw = pwcopy(user_pw); |
|
|
while (!skipargs && (ch = getopt(argc, argv, "d:f:l:u:cehR")) != -1) { |
while (!skipargs && (ch = getopt(argc, argv, |
|
"d:f:l:P:p:Q:u:cehR")) != -1) { |
switch (ch) { |
switch (ch) { |
|
case 'Q': |
|
if (strcasecmp(optarg, "requests") != 0) { |
|
fprintf(stderr, "Invalid query type\n"); |
|
exit(1); |
|
} |
|
for (i = 0; handlers[i].handler != NULL; i++) |
|
printf("%s\n", handlers[i].name); |
|
for (i = 0; extended_handlers[i].handler != NULL; i++) |
|
printf("%s\n", extended_handlers[i].name); |
|
exit(0); |
|
break; |
case 'R': |
case 'R': |
readonly = 1; |
readonly = 1; |
break; |
break; |
|
|
homedir = percent_expand(cp, "d", user_pw->pw_dir, |
homedir = percent_expand(cp, "d", user_pw->pw_dir, |
"u", user_pw->pw_name, (char *)NULL); |
"u", user_pw->pw_name, (char *)NULL); |
free(cp); |
free(cp); |
|
break; |
|
case 'p': |
|
if (request_whitelist != NULL) |
|
fatal("Permitted requests already set"); |
|
request_whitelist = xstrdup(optarg); |
|
break; |
|
case 'P': |
|
if (request_blacklist != NULL) |
|
fatal("Refused requests already set"); |
|
request_blacklist = xstrdup(optarg); |
break; |
break; |
case 'u': |
case 'u': |
errno = 0; |
errno = 0; |