version 1.6, 2000/09/07 20:27:53 |
version 1.6.2.2, 2001/02/19 17:19:24 |
|
|
#include "includes.h" |
#include "includes.h" |
RCSID("$OpenBSD$"); |
RCSID("$OpenBSD$"); |
|
|
#include "ssh.h" |
|
#include "buffer.h" |
#include "buffer.h" |
#include "bufaux.h" |
#include "bufaux.h" |
#include "getput.h" |
#include "getput.h" |
|
#include "log.h" |
#include "xmalloc.h" |
#include "xmalloc.h" |
|
|
/* version */ |
#include "sftp.h" |
#define SSH_FILEXFER_VERSION 2 |
#include "sftp-common.h" |
|
|
/* client to server */ |
|
#define SSH_FXP_INIT 1 |
|
#define SSH_FXP_OPEN 3 |
|
#define SSH_FXP_CLOSE 4 |
|
#define SSH_FXP_READ 5 |
|
#define SSH_FXP_WRITE 6 |
|
#define SSH_FXP_LSTAT 7 |
|
#define SSH_FXP_FSTAT 8 |
|
#define SSH_FXP_SETSTAT 9 |
|
#define SSH_FXP_FSETSTAT 10 |
|
#define SSH_FXP_OPENDIR 11 |
|
#define SSH_FXP_READDIR 12 |
|
#define SSH_FXP_REMOVE 13 |
|
#define SSH_FXP_MKDIR 14 |
|
#define SSH_FXP_RMDIR 15 |
|
#define SSH_FXP_REALPATH 16 |
|
#define SSH_FXP_STAT 17 |
|
#define SSH_FXP_RENAME 18 |
|
|
|
/* server to client */ |
|
#define SSH_FXP_VERSION 2 |
|
#define SSH_FXP_STATUS 101 |
|
#define SSH_FXP_HANDLE 102 |
|
#define SSH_FXP_DATA 103 |
|
#define SSH_FXP_NAME 104 |
|
#define SSH_FXP_ATTRS 105 |
|
|
|
/* portable open modes */ |
|
#define SSH_FXF_READ 0x01 |
|
#define SSH_FXF_WRITE 0x02 |
|
#define SSH_FXF_APPEND 0x04 |
|
#define SSH_FXF_CREAT 0x08 |
|
#define SSH_FXF_TRUNC 0x10 |
|
#define SSH_FXF_EXCL 0x20 |
|
|
|
/* attributes */ |
|
#define SSH_FXA_HAVE_SIZE 0x01 |
|
#define SSH_FXA_HAVE_UGID 0x02 |
|
#define SSH_FXA_HAVE_PERM 0x04 |
|
#define SSH_FXA_HAVE_TIME 0x08 |
|
|
|
/* status messages */ |
|
#define SSH_FX_OK 0x00 |
|
#define SSH_FX_EOF 0x01 |
|
#define SSH_FX_NO_SUCH_FILE 0x02 |
|
#define SSH_FX_PERMISSION_DENIED 0x03 |
|
#define SSH_FX_FAILURE 0x04 |
|
#define SSH_FX_BAD_MESSAGE 0x05 |
|
#define SSH_FX_NO_CONNECTION 0x06 |
|
#define SSH_FX_CONNECTION_LOST 0x07 |
|
|
|
|
|
/* helper */ |
/* helper */ |
|
#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 log |
#define TRACE debug |
|
|
/* input and output queue */ |
/* input and output queue */ |
Buffer iqueue; |
Buffer iqueue; |
|
|
|
|
/* portable attibutes, etc. */ |
/* portable attibutes, etc. */ |
|
|
typedef struct Attrib Attrib; |
|
typedef struct Stat Stat; |
typedef struct Stat Stat; |
|
|
struct Attrib |
struct Stat { |
{ |
|
u_int32_t flags; |
|
u_int32_t size_high; |
|
u_int32_t size_low; |
|
u_int64_t size; |
|
u_int32_t uid; |
|
u_int32_t gid; |
|
u_int32_t perm; |
|
u_int32_t atime; |
|
u_int32_t mtime; |
|
}; |
|
|
|
struct Stat |
|
{ |
|
char *name; |
char *name; |
char *long_name; |
char *long_name; |
Attrib attrib; |
Attrib attrib; |
|
|
int ret = 0; |
int ret = 0; |
switch (unixerrno) { |
switch (unixerrno) { |
case 0: |
case 0: |
ret = SSH_FX_OK; |
ret = SSH2_FX_OK; |
break; |
break; |
case ENOENT: |
case ENOENT: |
case ENOTDIR: |
case ENOTDIR: |
case EBADF: |
case EBADF: |
case ELOOP: |
case ELOOP: |
ret = SSH_FX_NO_SUCH_FILE; |
ret = SSH2_FX_NO_SUCH_FILE; |
break; |
break; |
case EPERM: |
case EPERM: |
case EACCES: |
case EACCES: |
case EFAULT: |
case EFAULT: |
ret = SSH_FX_PERMISSION_DENIED; |
ret = SSH2_FX_PERMISSION_DENIED; |
break; |
break; |
case ENAMETOOLONG: |
case ENAMETOOLONG: |
case EINVAL: |
case EINVAL: |
ret = SSH_FX_BAD_MESSAGE; |
ret = SSH2_FX_BAD_MESSAGE; |
break; |
break; |
default: |
default: |
ret = SSH_FX_FAILURE; |
ret = SSH2_FX_FAILURE; |
break; |
break; |
} |
} |
return ret; |
return ret; |
|
|
flags_from_portable(int pflags) |
flags_from_portable(int pflags) |
{ |
{ |
int flags = 0; |
int flags = 0; |
if (pflags & SSH_FXF_READ && |
if (pflags & SSH2_FXF_READ && |
pflags & SSH_FXF_WRITE) { |
pflags & SSH2_FXF_WRITE) { |
flags = O_RDWR; |
flags = O_RDWR; |
} else if (pflags & SSH_FXF_READ) { |
} else if (pflags & SSH2_FXF_READ) { |
flags = O_RDONLY; |
flags = O_RDONLY; |
} else if (pflags & SSH_FXF_WRITE) { |
} else if (pflags & SSH2_FXF_WRITE) { |
flags = O_WRONLY; |
flags = O_WRONLY; |
} |
} |
if (pflags & SSH_FXF_CREAT) |
if (pflags & SSH2_FXF_CREAT) |
flags |= O_CREAT; |
flags |= O_CREAT; |
if (pflags & SSH_FXF_TRUNC) |
if (pflags & SSH2_FXF_TRUNC) |
flags |= O_TRUNC; |
flags |= O_TRUNC; |
if (pflags & SSH_FXF_EXCL) |
if (pflags & SSH2_FXF_EXCL) |
flags |= O_EXCL; |
flags |= O_EXCL; |
return flags; |
return flags; |
} |
} |
|
|
void |
|
attrib_clear(Attrib *a) |
|
{ |
|
a->flags = 0; |
|
a->size_low = 0; |
|
a->size_high = 0; |
|
a->size = 0; |
|
a->uid = 0; |
|
a->gid = 0; |
|
a->perm = 0; |
|
a->atime = 0; |
|
a->mtime = 0; |
|
} |
|
|
|
Attrib * |
Attrib * |
decode_attrib(Buffer *b) |
|
{ |
|
static Attrib a; |
|
attrib_clear(&a); |
|
a.flags = buffer_get_int(b); |
|
if (a.flags & SSH_FXA_HAVE_SIZE) { |
|
a.size_high = buffer_get_int(b); |
|
a.size_low = buffer_get_int(b); |
|
a.size = (((u_int64_t) a.size_high) << 32) + a.size_low; |
|
} |
|
if (a.flags & SSH_FXA_HAVE_UGID) { |
|
a.uid = buffer_get_int(b); |
|
a.gid = buffer_get_int(b); |
|
} |
|
if (a.flags & SSH_FXA_HAVE_PERM) { |
|
a.perm = buffer_get_int(b); |
|
} |
|
if (a.flags & SSH_FXA_HAVE_TIME) { |
|
a.atime = buffer_get_int(b); |
|
a.mtime = buffer_get_int(b); |
|
} |
|
return &a; |
|
} |
|
|
|
void |
|
encode_attrib(Buffer *b, Attrib *a) |
|
{ |
|
buffer_put_int(b, a->flags); |
|
if (a->flags & SSH_FXA_HAVE_SIZE) { |
|
buffer_put_int(b, a->size_high); |
|
buffer_put_int(b, a->size_low); |
|
} |
|
if (a->flags & SSH_FXA_HAVE_UGID) { |
|
buffer_put_int(b, a->uid); |
|
buffer_put_int(b, a->gid); |
|
} |
|
if (a->flags & SSH_FXA_HAVE_PERM) { |
|
buffer_put_int(b, a->perm); |
|
} |
|
if (a->flags & SSH_FXA_HAVE_TIME) { |
|
buffer_put_int(b, a->atime); |
|
buffer_put_int(b, a->mtime); |
|
} |
|
} |
|
|
|
Attrib * |
|
stat_to_attrib(struct stat *st) |
|
{ |
|
static Attrib a; |
|
attrib_clear(&a); |
|
a.flags = 0; |
|
a.flags |= SSH_FXA_HAVE_SIZE; |
|
a.size = st->st_size; |
|
a.size_low = a.size; |
|
a.size_high = (u_int32_t) (a.size >> 32); |
|
a.flags |= SSH_FXA_HAVE_UGID; |
|
a.uid = st->st_uid; |
|
a.gid = st->st_gid; |
|
a.flags |= SSH_FXA_HAVE_PERM; |
|
a.perm = st->st_mode; |
|
a.flags |= SSH_FXA_HAVE_TIME; |
|
a.atime = st->st_atime; |
|
a.mtime = st->st_mtime; |
|
return &a; |
|
} |
|
|
|
Attrib * |
|
get_attrib(void) |
get_attrib(void) |
{ |
{ |
return decode_attrib(&iqueue); |
return decode_attrib(&iqueue); |
|
|
int |
int |
handle_is_ok(int i, int type) |
handle_is_ok(int i, int type) |
{ |
{ |
return i >= 0 && i < sizeof(handles)/sizeof(Handle) && handles[i].use == type; |
return i >= 0 && i < sizeof(handles)/sizeof(Handle) && |
|
handles[i].use == type; |
} |
} |
|
|
int |
int |
handle_to_string(int handle, char **stringp, int *hlenp) |
handle_to_string(int handle, char **stringp, int *hlenp) |
{ |
{ |
char buf[1024]; |
|
if (stringp == NULL || hlenp == NULL) |
if (stringp == NULL || hlenp == NULL) |
return -1; |
return -1; |
snprintf(buf, sizeof buf, "%d", handle); |
*stringp = xmalloc(sizeof(int32_t)); |
*stringp = xstrdup(buf); |
PUT_32BIT(*stringp, handle); |
*hlenp = strlen(*stringp); |
*hlenp = sizeof(int32_t); |
return 0; |
return 0; |
} |
} |
|
|
int |
int |
handle_from_string(char *handle, u_int hlen) |
handle_from_string(char *handle, u_int hlen) |
{ |
{ |
/* XXX OVERFLOW ? */ |
int val; |
char *ep; |
if (hlen != sizeof(int32_t)) |
long lval = strtol(handle, &ep, 10); |
|
int val = lval; |
|
if (*ep != '\0') |
|
return -1; |
return -1; |
|
val = GET_32BIT(handle); |
if (handle_is_ok(val, HANDLE_FILE) || |
if (handle_is_ok(val, HANDLE_FILE) || |
handle_is_ok(val, HANDLE_DIR)) |
handle_is_ok(val, HANDLE_DIR)) |
return val; |
return val; |
|
|
int |
int |
handle_to_fd(int handle) |
handle_to_fd(int handle) |
{ |
{ |
if (handle_is_ok(handle, HANDLE_FILE)) |
if (handle_is_ok(handle, HANDLE_FILE)) |
return handles[handle].fd; |
return handles[handle].fd; |
return -1; |
return -1; |
} |
} |
|
|
get_handle(void) |
get_handle(void) |
{ |
{ |
char *handle; |
char *handle; |
int val; |
int val = -1; |
u_int hlen; |
u_int hlen; |
handle = get_string(&hlen); |
handle = get_string(&hlen); |
val = handle_from_string(handle, hlen); |
if (hlen < 256) |
|
val = handle_from_string(handle, hlen); |
xfree(handle); |
xfree(handle); |
return val; |
return val; |
} |
} |
|
|
Buffer msg; |
Buffer msg; |
TRACE("sent status id %d error %d", id, error); |
TRACE("sent status id %d error %d", id, error); |
buffer_init(&msg); |
buffer_init(&msg); |
buffer_put_char(&msg, SSH_FXP_STATUS); |
buffer_put_char(&msg, SSH2_FXP_STATUS); |
buffer_put_int(&msg, id); |
buffer_put_int(&msg, id); |
buffer_put_int(&msg, error); |
buffer_put_int(&msg, error); |
send_msg(&msg); |
send_msg(&msg); |
|
|
send_data(u_int32_t id, char *data, int dlen) |
send_data(u_int32_t id, char *data, int dlen) |
{ |
{ |
TRACE("sent data id %d len %d", id, dlen); |
TRACE("sent data id %d len %d", id, dlen); |
send_data_or_handle(SSH_FXP_DATA, id, data, dlen); |
send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); |
} |
} |
|
|
void |
void |
|
|
int hlen; |
int hlen; |
handle_to_string(handle, &string, &hlen); |
handle_to_string(handle, &string, &hlen); |
TRACE("sent handle id %d handle %d", id, handle); |
TRACE("sent handle id %d handle %d", id, handle); |
send_data_or_handle(SSH_FXP_HANDLE, id, string, hlen); |
send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); |
xfree(string); |
xfree(string); |
} |
} |
|
|
|
|
Buffer msg; |
Buffer msg; |
int i; |
int i; |
buffer_init(&msg); |
buffer_init(&msg); |
buffer_put_char(&msg, SSH_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 %d count %d", id, count); |
TRACE("sent names id %d count %d", id, count); |
|
|
Buffer msg; |
Buffer msg; |
TRACE("sent attrib id %d have 0x%x", id, a->flags); |
TRACE("sent attrib id %d have 0x%x", id, a->flags); |
buffer_init(&msg); |
buffer_init(&msg); |
buffer_put_char(&msg, SSH_FXP_ATTRS); |
buffer_put_char(&msg, SSH2_FXP_ATTRS); |
buffer_put_int(&msg, id); |
buffer_put_int(&msg, id); |
encode_attrib(&msg, a); |
encode_attrib(&msg, a); |
send_msg(&msg); |
send_msg(&msg); |
|
|
|
|
TRACE("client version %d", version); |
TRACE("client version %d", version); |
buffer_init(&msg); |
buffer_init(&msg); |
buffer_put_char(&msg, SSH_FXP_VERSION); |
buffer_put_char(&msg, SSH2_FXP_VERSION); |
buffer_put_int(&msg, SSH_FILEXFER_VERSION); |
buffer_put_int(&msg, SSH2_FILEXFER_VERSION); |
send_msg(&msg); |
send_msg(&msg); |
buffer_free(&msg); |
buffer_free(&msg); |
} |
} |
|
|
u_int32_t id, pflags; |
u_int32_t id, pflags; |
Attrib *a; |
Attrib *a; |
char *name; |
char *name; |
int handle, fd, flags, mode, status = SSH_FX_FAILURE; |
int handle, fd, flags, mode, status = SSH2_FX_FAILURE; |
|
|
id = get_int(); |
id = get_int(); |
name = get_string(NULL); |
name = get_string(NULL); |
pflags = get_int(); |
pflags = get_int(); /* portable flags */ |
a = get_attrib(); |
a = get_attrib(); |
flags = flags_from_portable(pflags); |
flags = flags_from_portable(pflags); |
mode = (a->flags & SSH_FXA_HAVE_PERM) ? a->perm : 0666; |
mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; |
TRACE("open id %d name %s flags %d mode 0%o", id, name, pflags, mode); |
TRACE("open id %d name %s flags %d mode 0%o", id, name, pflags, mode); |
fd = open(name, flags, mode); |
fd = open(name, flags, mode); |
if (fd < 0) { |
if (fd < 0) { |
|
|
close(fd); |
close(fd); |
} else { |
} else { |
send_handle(id, handle); |
send_handle(id, handle); |
status = SSH_FX_OK; |
status = SSH2_FX_OK; |
} |
} |
} |
} |
if (status != SSH_FX_OK) |
if (status != SSH2_FX_OK) |
send_status(id, status); |
send_status(id, status); |
xfree(name); |
xfree(name); |
} |
} |
|
|
process_close(void) |
process_close(void) |
{ |
{ |
u_int32_t id; |
u_int32_t id; |
int handle, ret, status = SSH_FX_FAILURE; |
int handle, ret, status = SSH2_FX_FAILURE; |
|
|
id = get_int(); |
id = get_int(); |
handle = get_handle(); |
handle = get_handle(); |
TRACE("close id %d handle %d", id, handle); |
TRACE("close id %d handle %d", id, handle); |
ret = handle_close(handle); |
ret = handle_close(handle); |
status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK; |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
send_status(id, status); |
send_status(id, status); |
} |
} |
|
|
|
|
process_read(void) |
process_read(void) |
{ |
{ |
char buf[64*1024]; |
char buf[64*1024]; |
u_int32_t id, off_high, off_low, len; |
u_int32_t id, len; |
int handle, fd, ret, status = SSH_FX_FAILURE; |
int handle, fd, ret, status = SSH2_FX_FAILURE; |
u_int64_t off; |
u_int64_t off; |
|
|
id = get_int(); |
id = get_int(); |
handle = get_handle(); |
handle = get_handle(); |
off_high = get_int(); |
off = get_int64(); |
off_low = get_int(); |
|
len = get_int(); |
len = get_int(); |
|
|
off = (((u_int64_t) off_high) << 32) + off_low; |
TRACE("read id %d handle %d off %llu len %d", id, handle, |
TRACE("read id %d handle %d off %qd len %d", id, handle, off, len); |
(unsigned long long)off, len); |
if (len > sizeof buf) { |
if (len > sizeof buf) { |
len = sizeof buf; |
len = sizeof buf; |
log("read change len %d", len); |
log("read change len %d", len); |
|
|
if (ret < 0) { |
if (ret < 0) { |
status = errno_to_portable(errno); |
status = errno_to_portable(errno); |
} else if (ret == 0) { |
} else if (ret == 0) { |
status = SSH_FX_EOF; |
status = SSH2_FX_EOF; |
} else { |
} else { |
send_data(id, buf, ret); |
send_data(id, buf, ret); |
status = SSH_FX_OK; |
status = SSH2_FX_OK; |
} |
} |
} |
} |
} |
} |
if (status != SSH_FX_OK) |
if (status != SSH2_FX_OK) |
send_status(id, status); |
send_status(id, status); |
} |
} |
|
|
void |
void |
process_write(void) |
process_write(void) |
{ |
{ |
u_int32_t id, off_high, off_low; |
u_int32_t id; |
u_int64_t off; |
u_int64_t off; |
u_int len; |
u_int len; |
int handle, fd, ret, status = SSH_FX_FAILURE; |
int handle, fd, ret, status = SSH2_FX_FAILURE; |
char *data; |
char *data; |
|
|
id = get_int(); |
id = get_int(); |
handle = get_handle(); |
handle = get_handle(); |
off_high = get_int(); |
off = get_int64(); |
off_low = get_int(); |
|
data = get_string(&len); |
data = get_string(&len); |
|
|
off = (((u_int64_t) off_high) << 32) + off_low; |
TRACE("write id %d handle %d off %llu len %d", id, handle, |
TRACE("write id %d handle %d off %qd len %d", id, handle, off, len); |
(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) { |
|
|
error("process_write: write failed"); |
error("process_write: write failed"); |
status = errno_to_portable(errno); |
status = errno_to_portable(errno); |
} else if (ret == len) { |
} else if (ret == len) { |
status = SSH_FX_OK; |
status = SSH2_FX_OK; |
} else { |
} else { |
log("nothing at all written"); |
log("nothing at all written"); |
} |
} |
|
|
void |
void |
process_do_stat(int do_lstat) |
process_do_stat(int do_lstat) |
{ |
{ |
Attrib *a; |
Attrib a; |
struct stat st; |
struct stat st; |
u_int32_t id; |
u_int32_t id; |
char *name; |
char *name; |
int ret, status = SSH_FX_FAILURE; |
int ret, status = SSH2_FX_FAILURE; |
|
|
id = get_int(); |
id = get_int(); |
name = get_string(NULL); |
name = get_string(NULL); |
|
|
if (ret < 0) { |
if (ret < 0) { |
status = errno_to_portable(errno); |
status = errno_to_portable(errno); |
} else { |
} else { |
a = stat_to_attrib(&st); |
stat_to_attrib(&st, &a); |
send_attrib(id, a); |
send_attrib(id, &a); |
status = SSH_FX_OK; |
status = SSH2_FX_OK; |
} |
} |
if (status != SSH_FX_OK) |
if (status != SSH2_FX_OK) |
send_status(id, status); |
send_status(id, status); |
xfree(name); |
xfree(name); |
} |
} |
|
|
void |
void |
process_fstat(void) |
process_fstat(void) |
{ |
{ |
Attrib *a; |
Attrib a; |
struct stat st; |
struct stat st; |
u_int32_t id; |
u_int32_t id; |
int fd, ret, handle, status = SSH_FX_FAILURE; |
int fd, ret, handle, status = SSH2_FX_FAILURE; |
|
|
id = get_int(); |
id = get_int(); |
handle = get_handle(); |
handle = get_handle(); |
|
|
if (ret < 0) { |
if (ret < 0) { |
status = errno_to_portable(errno); |
status = errno_to_portable(errno); |
} else { |
} else { |
a = stat_to_attrib(&st); |
stat_to_attrib(&st, &a); |
send_attrib(id, a); |
send_attrib(id, &a); |
status = SSH_FX_OK; |
status = SSH2_FX_OK; |
} |
} |
} |
} |
if (status != SSH_FX_OK) |
if (status != SSH2_FX_OK) |
send_status(id, status); |
send_status(id, status); |
} |
} |
|
|
|
|
u_int32_t id; |
u_int32_t id; |
char *name; |
char *name; |
int ret; |
int ret; |
int status = SSH_FX_OK; |
int status = SSH2_FX_OK; |
|
|
id = get_int(); |
id = get_int(); |
name = get_string(NULL); |
name = get_string(NULL); |
a = get_attrib(); |
a = get_attrib(); |
TRACE("setstat id %d name %s", id, name); |
TRACE("setstat id %d name %s", id, name); |
if (a->flags & SSH_FXA_HAVE_PERM) { |
if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { |
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 & SSH_FXA_HAVE_TIME) { |
if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { |
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) { |
|
ret = chown(name, a->uid, a->gid); |
|
if (ret == -1) |
|
status = errno_to_portable(errno); |
|
} |
send_status(id, status); |
send_status(id, status); |
xfree(name); |
xfree(name); |
} |
} |
|
|
Attrib *a; |
Attrib *a; |
u_int32_t id; |
u_int32_t id; |
int handle, fd, ret; |
int handle, fd, ret; |
int status = SSH_FX_OK; |
int status = SSH2_FX_OK; |
|
|
id = get_int(); |
id = get_int(); |
handle = get_handle(); |
handle = get_handle(); |
|
|
TRACE("fsetstat id %d handle %d", id, handle); |
TRACE("fsetstat id %d handle %d", id, handle); |
fd = handle_to_fd(handle); |
fd = handle_to_fd(handle); |
if (fd < 0) { |
if (fd < 0) { |
status = SSH_FX_FAILURE; |
status = SSH2_FX_FAILURE; |
} else { |
} else { |
if (a->flags & SSH_FXA_HAVE_PERM) { |
if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { |
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 & SSH_FXA_HAVE_TIME) { |
if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { |
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) { |
|
ret = fchown(fd, a->uid, a->gid); |
|
if (ret == -1) |
|
status = errno_to_portable(errno); |
|
} |
} |
} |
send_status(id, status); |
send_status(id, status); |
} |
} |
|
|
{ |
{ |
DIR *dirp = NULL; |
DIR *dirp = NULL; |
char *path; |
char *path; |
int handle, status = SSH_FX_FAILURE; |
int handle, status = SSH2_FX_FAILURE; |
u_int32_t id; |
u_int32_t id; |
|
|
id = get_int(); |
id = get_int(); |
path = get_string(NULL); |
path = get_string(NULL); |
TRACE("opendir id %d path %s", id, path); |
TRACE("opendir id %d path %s", id, path); |
dirp = opendir(path); |
dirp = opendir(path); |
if (dirp == NULL) { |
if (dirp == NULL) { |
status = errno_to_portable(errno); |
status = errno_to_portable(errno); |
} else { |
} else { |
|
|
closedir(dirp); |
closedir(dirp); |
} else { |
} else { |
send_handle(id, handle); |
send_handle(id, handle); |
status = SSH_FX_OK; |
status = SSH2_FX_OK; |
} |
} |
|
|
} |
} |
if (status != SSH_FX_OK) |
if (status != SSH2_FX_OK) |
send_status(id, status); |
send_status(id, status); |
xfree(path); |
xfree(path); |
} |
} |
|
|
|
/* |
|
* drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh |
|
*/ |
char * |
char * |
ls_file(char *name, struct stat *st) |
ls_file(char *name, struct stat *st) |
{ |
{ |
char buf[1024]; |
int sz = 0; |
snprintf(buf, sizeof buf, "0%o %d %d %qd %d %s", |
struct passwd *pw; |
st->st_mode, st->st_uid, st->st_gid, (long long)st->st_size,(int) st->st_mtime, |
struct group *gr; |
name); |
struct tm *ltime = localtime(&st->st_mtime); |
|
char *user, *group; |
|
char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1]; |
|
|
|
strmode(st->st_mode, mode); |
|
if ((pw = getpwuid(st->st_uid)) != NULL) { |
|
user = pw->pw_name; |
|
} else { |
|
snprintf(ubuf, sizeof ubuf, "%d", st->st_uid); |
|
user = ubuf; |
|
} |
|
if ((gr = getgrgid(st->st_gid)) != NULL) { |
|
group = gr->gr_name; |
|
} else { |
|
snprintf(gbuf, sizeof gbuf, "%d", st->st_gid); |
|
group = gbuf; |
|
} |
|
if (ltime != NULL) { |
|
if (time(NULL) - st->st_mtime < (365*24*60*60)/2) |
|
sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime); |
|
else |
|
sz = strftime(tbuf, sizeof tbuf, "%b %e %Y", ltime); |
|
} |
|
if (sz == 0) |
|
tbuf[0] = '\0'; |
|
snprintf(buf, sizeof buf, "%s %3d %-8.8s %-8.8s %8llu %s %s", mode, |
|
st->st_nlink, user, group, (unsigned long long)st->st_size, tbuf, name); |
return xstrdup(buf); |
return xstrdup(buf); |
} |
} |
|
|
|
|
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, SSH_FX_FAILURE); |
send_status(id, SSH2_FX_FAILURE); |
} else { |
} else { |
Attrib *a; |
|
struct stat st; |
struct stat st; |
char pathname[1024]; |
char pathname[1024]; |
Stat *stats; |
Stat *stats; |
|
|
"%s/%s", path, dp->d_name); |
"%s/%s", path, dp->d_name); |
if (lstat(pathname, &st) < 0) |
if (lstat(pathname, &st) < 0) |
continue; |
continue; |
a = stat_to_attrib(&st); |
stat_to_attrib(&st, &(stats[count].attrib)); |
stats[count].attrib = *a; |
|
stats[count].name = xstrdup(dp->d_name); |
stats[count].name = xstrdup(dp->d_name); |
stats[count].long_name = ls_file(dp->d_name, &st); |
stats[count].long_name = ls_file(dp->d_name, &st); |
count++; |
count++; |
/* send up to 100 entries in one message */ |
/* send up to 100 entries in one message */ |
|
/* XXX check packet size instead */ |
if (count == 100) |
if (count == 100) |
break; |
break; |
} |
} |
send_names(id, count, stats); |
if (count > 0) { |
for(i = 0; i < count; i++) { |
send_names(id, count, stats); |
xfree(stats[i].name); |
for(i = 0; i < count; i++) { |
xfree(stats[i].long_name); |
xfree(stats[i].name); |
|
xfree(stats[i].long_name); |
|
} |
|
} else { |
|
send_status(id, SSH2_FX_EOF); |
} |
} |
xfree(stats); |
xfree(stats); |
} |
} |
|
|
{ |
{ |
char *name; |
char *name; |
u_int32_t id; |
u_int32_t id; |
int status = SSH_FX_FAILURE; |
int status = SSH2_FX_FAILURE; |
int ret; |
int ret; |
|
|
id = get_int(); |
id = get_int(); |
name = get_string(NULL); |
name = get_string(NULL); |
TRACE("remove id %d name %s", id, name); |
TRACE("remove id %d name %s", id, name); |
ret = remove(name); |
ret = unlink(name); |
status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK; |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
send_status(id, status); |
send_status(id, status); |
xfree(name); |
xfree(name); |
} |
} |
|
|
Attrib *a; |
Attrib *a; |
u_int32_t id; |
u_int32_t id; |
char *name; |
char *name; |
int ret, mode, status = SSH_FX_FAILURE; |
int ret, mode, status = SSH2_FX_FAILURE; |
|
|
id = get_int(); |
id = get_int(); |
name = get_string(NULL); |
name = get_string(NULL); |
a = get_attrib(); |
a = get_attrib(); |
mode = (a->flags & SSH_FXA_HAVE_PERM) ? a->perm & 0777 : 0777; |
mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? |
|
a->perm & 0777 : 0777; |
TRACE("mkdir id %d name %s mode 0%o", id, name, mode); |
TRACE("mkdir id %d name %s mode 0%o", id, name, mode); |
ret = mkdir(name, mode); |
ret = mkdir(name, mode); |
status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK; |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
send_status(id, status); |
send_status(id, status); |
xfree(name); |
xfree(name); |
} |
} |
|
|
name = get_string(NULL); |
name = get_string(NULL); |
TRACE("rmdir id %d name %s", id, name); |
TRACE("rmdir id %d name %s", id, name); |
ret = rmdir(name); |
ret = rmdir(name); |
status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK; |
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
send_status(id, status); |
send_status(id, status); |
xfree(name); |
xfree(name); |
} |
} |
|
|
|
|
id = get_int(); |
id = get_int(); |
path = get_string(NULL); |
path = get_string(NULL); |
|
if (path[0] == '\0') { |
|
xfree(path); |
|
path = xstrdup("."); |
|
} |
TRACE("realpath id %d path %s", id, path); |
TRACE("realpath id %d path %s", id, 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)); |
|
|
process_rename(void) |
process_rename(void) |
{ |
{ |
u_int32_t id; |
u_int32_t id; |
|
struct stat st; |
char *oldpath, *newpath; |
char *oldpath, *newpath; |
int ret, status; |
int ret, status = SSH2_FX_FAILURE; |
|
|
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 %d old %s new %s", id, oldpath, newpath); |
TRACE("rename id %d old %s new %s", id, oldpath, newpath); |
ret = rename(oldpath, newpath); |
/* fail if 'newpath' exists */ |
status = (ret == -1) ? errno_to_portable(errno) : SSH_FX_OK; |
if (stat(newpath, &st) == -1) { |
|
ret = rename(oldpath, newpath); |
|
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
|
} |
send_status(id, status); |
send_status(id, status); |
xfree(oldpath); |
xfree(oldpath); |
xfree(newpath); |
xfree(newpath); |
} |
} |
|
|
|
void |
|
process_extended(void) |
|
{ |
|
u_int32_t id; |
|
char *request; |
|
|
|
id = get_int(); |
|
request = get_string(NULL); |
|
send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ |
|
xfree(request); |
|
} |
|
|
/* stolen from ssh-agent */ |
/* stolen from ssh-agent */ |
|
|
void |
void |
process(void) |
process(void) |
{ |
{ |
unsigned int msg_len; |
u_int msg_len; |
unsigned int type; |
u_int type; |
unsigned char *cp; |
u_char *cp; |
|
|
if (buffer_len(&iqueue) < 5) |
if (buffer_len(&iqueue) < 5) |
return; /* Incomplete message. */ |
return; /* Incomplete message. */ |
cp = (unsigned char *) buffer_ptr(&iqueue); |
cp = (u_char *) buffer_ptr(&iqueue); |
msg_len = GET_32BIT(cp); |
msg_len = GET_32BIT(cp); |
if (msg_len > 256 * 1024) { |
if (msg_len > 256 * 1024) { |
error("bad message "); |
error("bad message "); |
|
|
buffer_consume(&iqueue, 4); |
buffer_consume(&iqueue, 4); |
type = buffer_get_char(&iqueue); |
type = buffer_get_char(&iqueue); |
switch (type) { |
switch (type) { |
case SSH_FXP_INIT: |
case SSH2_FXP_INIT: |
process_init(); |
process_init(); |
break; |
break; |
case SSH_FXP_OPEN: |
case SSH2_FXP_OPEN: |
process_open(); |
process_open(); |
break; |
break; |
case SSH_FXP_CLOSE: |
case SSH2_FXP_CLOSE: |
process_close(); |
process_close(); |
break; |
break; |
case SSH_FXP_READ: |
case SSH2_FXP_READ: |
process_read(); |
process_read(); |
break; |
break; |
case SSH_FXP_WRITE: |
case SSH2_FXP_WRITE: |
process_write(); |
process_write(); |
break; |
break; |
case SSH_FXP_LSTAT: |
case SSH2_FXP_LSTAT: |
process_lstat(); |
process_lstat(); |
break; |
break; |
case SSH_FXP_FSTAT: |
case SSH2_FXP_FSTAT: |
process_fstat(); |
process_fstat(); |
break; |
break; |
case SSH_FXP_SETSTAT: |
case SSH2_FXP_SETSTAT: |
process_setstat(); |
process_setstat(); |
break; |
break; |
case SSH_FXP_FSETSTAT: |
case SSH2_FXP_FSETSTAT: |
process_fsetstat(); |
process_fsetstat(); |
break; |
break; |
case SSH_FXP_OPENDIR: |
case SSH2_FXP_OPENDIR: |
process_opendir(); |
process_opendir(); |
break; |
break; |
case SSH_FXP_READDIR: |
case SSH2_FXP_READDIR: |
process_readdir(); |
process_readdir(); |
break; |
break; |
case SSH_FXP_REMOVE: |
case SSH2_FXP_REMOVE: |
process_remove(); |
process_remove(); |
break; |
break; |
case SSH_FXP_MKDIR: |
case SSH2_FXP_MKDIR: |
process_mkdir(); |
process_mkdir(); |
break; |
break; |
case SSH_FXP_RMDIR: |
case SSH2_FXP_RMDIR: |
process_rmdir(); |
process_rmdir(); |
break; |
break; |
case SSH_FXP_REALPATH: |
case SSH2_FXP_REALPATH: |
process_realpath(); |
process_realpath(); |
break; |
break; |
case SSH_FXP_STAT: |
case SSH2_FXP_STAT: |
process_stat(); |
process_stat(); |
break; |
break; |
case SSH_FXP_RENAME: |
case SSH2_FXP_RENAME: |
process_rename(); |
process_rename(); |
break; |
break; |
|
case SSH2_FXP_EXTENDED: |
|
process_extended(); |
|
break; |
default: |
default: |
error("Unknown message %d", type); |
error("Unknown message %d", type); |
break; |
break; |
|
|
ssize_t len, olen; |
ssize_t len, olen; |
|
|
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); |