version 1.6, 2000/09/07 20:27:53 |
version 1.6.2.4, 2001/05/07 21:09:34 |
|
|
#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; |
Buffer oqueue; |
Buffer oqueue; |
|
|
|
/* Version of client */ |
|
int version; |
|
|
/* 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; |
|
|
errno_to_portable(int unixerrno) |
errno_to_portable(int unixerrno) |
{ |
{ |
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 && |
|
pflags & SSH_FXF_WRITE) { |
if ((pflags & SSH2_FXF_READ) && |
|
(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 fd; |
int fd; |
char *name; |
char *name; |
}; |
}; |
|
|
enum { |
enum { |
HANDLE_UNUSED, |
HANDLE_UNUSED, |
HANDLE_DIR, |
HANDLE_DIR, |
HANDLE_FILE |
HANDLE_FILE |
}; |
}; |
|
|
Handle handles[100]; |
Handle handles[100]; |
|
|
void |
void |
handle_init(void) |
handle_init(void) |
{ |
{ |
int i; |
int i; |
|
|
for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) |
for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) |
handles[i].use = HANDLE_UNUSED; |
handles[i].use = HANDLE_UNUSED; |
} |
} |
|
|
handle_new(int use, char *name, int fd, DIR *dirp) |
handle_new(int use, char *name, int fd, DIR *dirp) |
{ |
{ |
int i; |
int i; |
|
|
for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) { |
for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) { |
if (handles[i].use == HANDLE_UNUSED) { |
if (handles[i].use == HANDLE_UNUSED) { |
handles[i].use = use; |
handles[i].use = use; |
|
|
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; |
|
long lval = strtol(handle, &ep, 10); |
if (hlen != sizeof(int32_t)) |
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; |
} |
} |
|
|
handle_close(int handle) |
handle_close(int handle) |
{ |
{ |
int ret = -1; |
int ret = -1; |
|
|
if (handle_is_ok(handle, HANDLE_FILE)) { |
if (handle_is_ok(handle, HANDLE_FILE)) { |
ret = close(handles[handle].fd); |
ret = close(handles[handle].fd); |
handles[handle].use = HANDLE_UNUSED; |
handles[handle].use = HANDLE_UNUSED; |
|
|
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; |
} |
} |
|
|
send_msg(Buffer *m) |
send_msg(Buffer *m) |
{ |
{ |
int mlen = buffer_len(m); |
int mlen = buffer_len(m); |
|
|
buffer_put_int(&oqueue, mlen); |
buffer_put_int(&oqueue, mlen); |
buffer_append(&oqueue, buffer_ptr(m), mlen); |
buffer_append(&oqueue, buffer_ptr(m), mlen); |
buffer_consume(m, mlen); |
buffer_consume(m, mlen); |
|
|
send_status(u_int32_t id, u_int32_t error) |
send_status(u_int32_t id, u_int32_t error) |
{ |
{ |
Buffer msg; |
Buffer msg; |
|
const char *status_messages[] = { |
|
"Success", /* SSH_FX_OK */ |
|
"End of file", /* SSH_FX_EOF */ |
|
"No such file", /* SSH_FX_NO_SUCH_FILE */ |
|
"Permission denied", /* SSH_FX_PERMISSION_DENIED */ |
|
"Failure", /* SSH_FX_FAILURE */ |
|
"Bad message", /* SSH_FX_BAD_MESSAGE */ |
|
"No connection", /* SSH_FX_NO_CONNECTION */ |
|
"Connection lost", /* SSH_FX_CONNECTION_LOST */ |
|
"Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ |
|
"Unknown error" /* Others */ |
|
}; |
|
|
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); |
|
if (version >= 3) { |
|
buffer_put_cstring(&msg, |
|
status_messages[MIN(error,SSH2_FX_MAX)]); |
|
buffer_put_cstring(&msg, ""); |
|
} |
send_msg(&msg); |
send_msg(&msg); |
buffer_free(&msg); |
buffer_free(&msg); |
} |
} |
|
|
send_data_or_handle(char type, u_int32_t id, char *data, int dlen) |
send_data_or_handle(char type, u_int32_t id, char *data, int dlen) |
{ |
{ |
Buffer msg; |
Buffer msg; |
|
|
buffer_init(&msg); |
buffer_init(&msg); |
buffer_put_char(&msg, type); |
buffer_put_char(&msg, type); |
buffer_put_int(&msg, id); |
buffer_put_int(&msg, id); |
|
|
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 |
|
|
{ |
{ |
char *string; |
char *string; |
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); |
|
|
send_attrib(u_int32_t id, Attrib *a) |
send_attrib(u_int32_t id, Attrib *a) |
{ |
{ |
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); |
|
|
process_init(void) |
process_init(void) |
{ |
{ |
Buffer msg; |
Buffer msg; |
int version = buffer_get_int(&iqueue); |
|
|
|
|
version = buffer_get_int(&iqueue); |
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); |
} |
} |
|
|
|
|
attrib_to_tv(Attrib *a) |
attrib_to_tv(Attrib *a) |
{ |
{ |
static struct timeval tv[2]; |
static struct timeval tv[2]; |
|
|
tv[0].tv_sec = a->atime; |
tv[0].tv_sec = a->atime; |
tv[0].tv_usec = 0; |
tv[0].tv_usec = 0; |
tv[1].tv_sec = a->mtime; |
tv[1].tv_sec = a->mtime; |
|
|
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_readlink(void) |
|
{ |
|
u_int32_t id; |
|
char link[MAXPATHLEN]; |
|
char *path; |
|
|
|
id = get_int(); |
|
path = get_string(NULL); |
|
TRACE("readlink id %d path %s", id, path); |
|
if (readlink(path, link, sizeof(link) - 1) == -1) |
|
send_status(id, errno_to_portable(errno)); |
|
else { |
|
Stat s; |
|
|
|
link[sizeof(link) - 1] = '\0'; |
|
attrib_clear(&s.attrib); |
|
s.name = s.long_name = link; |
|
send_names(id, 1, &s); |
|
} |
|
xfree(path); |
|
} |
|
|
|
void |
|
process_symlink(void) |
|
{ |
|
u_int32_t id; |
|
struct stat st; |
|
char *oldpath, *newpath; |
|
int ret, status = SSH2_FX_FAILURE; |
|
|
|
id = get_int(); |
|
oldpath = get_string(NULL); |
|
newpath = get_string(NULL); |
|
TRACE("symlink id %d old %s new %s", id, oldpath, newpath); |
|
/* fail if 'newpath' exists */ |
|
if (stat(newpath, &st) == -1) { |
|
ret = symlink(oldpath, newpath); |
|
status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; |
|
} |
|
send_status(id, status); |
|
xfree(oldpath); |
|
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_READLINK: |
|
process_readlink(); |
|
break; |
|
case SSH2_FXP_SYMLINK: |
|
process_symlink(); |
|
break; |
|
case SSH2_FXP_EXTENDED: |
|
process_extended(); |
|
break; |
default: |
default: |
error("Unknown message %d", type); |
error("Unknown message %d", type); |
break; |
break; |
|
|
int |
int |
main(int ac, char **av) |
main(int ac, char **av) |
{ |
{ |
fd_set rset, wset; |
fd_set *rset, *wset; |
int in, out, max; |
int in, out, max; |
ssize_t len, olen; |
ssize_t len, olen, set_size; |
|
|
|
/* XXX should use getopt */ |
|
|
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); |
|
|
|
|
buffer_init(&iqueue); |
buffer_init(&iqueue); |
buffer_init(&oqueue); |
buffer_init(&oqueue); |
|
|
|
set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); |
|
rset = (fd_set *)xmalloc(set_size); |
|
wset = (fd_set *)xmalloc(set_size); |
|
|
for (;;) { |
for (;;) { |
FD_ZERO(&rset); |
memset(rset, 0, set_size); |
FD_ZERO(&wset); |
memset(wset, 0, set_size); |
|
|
FD_SET(in, &rset); |
FD_SET(in, rset); |
olen = buffer_len(&oqueue); |
olen = buffer_len(&oqueue); |
if (olen > 0) |
if (olen > 0) |
FD_SET(out, &wset); |
FD_SET(out, wset); |
|
|
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); |
exit(2); |
} |
} |
|
|
/* copy stdin to iqueue */ |
/* copy stdin to iqueue */ |
if (FD_ISSET(in, &rset)) { |
if (FD_ISSET(in, rset)) { |
char buf[4*4096]; |
char buf[4*4096]; |
len = read(in, buf, sizeof buf); |
len = read(in, buf, sizeof buf); |
if (len == 0) { |
if (len == 0) { |
|
|
} |
} |
} |
} |
/* send oqueue to stdout */ |
/* send oqueue to stdout */ |
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 error"); |