Commit 833f21aa authored by Niels Möller's avatar Niels Möller

* src/sftp: New directory. Not compiled or used by default.

Rev: src/sftp/.c-style:1.1
Rev: src/sftp/Makefile:1.1
Rev: src/sftp/Makefile.am:1.1
Rev: src/sftp/buffer.c:1.1
Rev: src/sftp/buffer.h:1.1
Rev: src/sftp/sftp-server:1.1
Rev: src/sftp/sftp-server.c:1.1
Rev: src/sftp/sftp-test-client:1.1
Rev: src/sftp/sftp-test-client.c:1.1
Rev: src/sftp/sftp.h:1.1
parent e9bddc64
[attr]binary -text -crlf -diff -ident
* text ident
sftp-server binary
sftp-test-client binary
CC=gcc
CFLAGS = -O2 -ggdb3 -Wall
CPPFLAGS = -DHAVE_CONFIG_H -I.. -I../..
OBJS = buffer.o
HEADERS = buffer.h
all: sftp-server sftp-test-client
%.o: %.c $(HEADERS)
$(CC) $(CFLAGS) $(CPPFLAGS) -c $<
%: %.o $(OBJS)
$(CC) -o $@ $^
# sftp-server: sftp-server.o buffer.o
# sftp-server: sftp-server.o buffer.o
#
# sftp-server.o: sftp-server.c buffer.h
bin_PROGRAMS = sftp-server
sftp_server_SOURCES = sftp_server.c buffer.c
/* buffer.c
*
* $Id$
*
* Buffering for sftp.
*/
#include "buffer.h"
#include <assert.h>
#if LSH
#include <stdlib.h>
struct sftp_input
{
FILE *f;
UINT32 left;
};
struct sftp_output
{
FILE *f;
/* The message type is the first byte of a message, after the
* length. */
UINT8 msg;
/* The next word is either the id, or the version. */
UINT32 first;
/* The rest of the packet is variable length. */
UINT8 *data;
UINT32 size;
UINT32 i;
};
/* Input */
static int
sftp_check_input(struct sftp_input *input, UINT32 length)
{
if (input->left < length)
return 0;
input->left -= length;
return 1;
}
int
sftp_get_data(struct sftp_input *i, UINT32 length, UINT8 *data)
{
return sftp_check_input(i, length)
&& (fread(data, 1, length, i->f) == length);
}
#define GET_DATA(i, buf) \
(sftp_get_data((i), sizeof((buf)), (buf)))
int
sftp_get_uint8(struct sftp_input *i, UINT8 *value)
{
return sftp_get_data(i, 1, value);
}
int
sftp_get_uint32(struct sftp_input *i, UINT32 *value)
{
UINT8 buf[4];
if (!GET_DATA(i, buf))
return 0;
*value = READ_UINT32(buf);
return 1;
}
#define READ_UINT64(p) \
( (((UINT64) (p)[0]) << 56) \
| (((UINT64) (p)[1]) << 48) \
| (((UINT64) (p)[2]) << 40) \
| (((UINT64) (p)[3]) << 32) \
| (((UINT64) (p)[4]) << 24) \
| (((UINT64) (p)[5]) << 16) \
| (((UINT64) (p)[6]) << 8) \
| ((UINT64) (p)[7]))
int
sftp_get_uint64(struct sftp_input *i, UINT64 *value)
{
UINT8 buf[8];
if (!GET_DATA(i, buf))
return 0;
*value = READ_UINT64(buf);
return 1;
}
UINT8 *
sftp_get_string(struct sftp_input *i, UINT32 *length)
{
UINT8 *data;
if (!(sftp_get_uint32(i, length) && sftp_check_input(i, *length)))
return NULL;
data = malloc(*length + 1);
if (!data)
return NULL;
if (!sftp_get_data(i, *length, data))
{
free(data);
return NULL;
}
/* NUL-terminate, for convenience */
data[*length] = '\0';
return data;
}
void
sftp_free_string(UINT8 *data)
{
free(data);
}
int sftp_get_eod(struct sftp_input *i)
{
return !i->left;
}
/* Input */
struct sftp_input *
sftp_make_input(FILE *f)
{
struct sftp_input *i = malloc(sizeof(struct sftp_input));
if (i)
{
i->f = f;
i->left = 0;
}
return i;
}
/* Returns 1 of all was well, 0 on error, and -1 on EOF */
int
sftp_read_packet(struct sftp_input *i)
{
UINT8 buf[4];
int done;
assert(i->left == 0);
done = fread(buf, 1, sizeof(buf), i->f);
switch (done)
{
case 0:
return feof(i->f) ? -1 : 0;
case 4:
i->left = READ_UINT32(buf);
return 1;
default:
return 0;
}
}
/* Output */
static int
sftp_check_output(struct sftp_output *o, UINT32 length)
{
UINT32 needed = o->i + length;
if (!o->data || (needed > o->size))
{
UINT8 *p;
UINT32 size = 2 * needed + 40;
p = realloc(o->data, size);
if (!p)
return 0;
o->data = p;
o->size = size;
}
return 1;
}
int
sftp_put_data(struct sftp_output *o, UINT32 length, const UINT8 *data)
{
if (!sftp_check_output(o, length))
return 0;
memcpy(o->data + o->i, data, length);
o->i += length;
return 1;
}
#define PUT_DATA(o, buf) \
(sftp_put_data((o), sizeof((buf)), (buf)))
int
sftp_put_uint8(struct sftp_output *o, UINT8 value)
{
if (!sftp_check_output(o, 1))
return 0;
o->data[o->i++] = value;
return 1;
}
int
sftp_put_uint32(struct sftp_output *o, UINT32 value)
{
UINT8 buf[4];
WRITE_UINT32(buf, value);
return PUT_DATA(o, buf);
}
#define WRITE_UINT64(p, i) \
do { \
(p)[0] = ((i) >> 56) & 0xff; \
(p)[1] = ((i) >> 48) & 0xff; \
(p)[2] = ((i) >> 40) & 0xff; \
(p)[3] = ((i) >> 32) & 0xff; \
(p)[4] = ((i) >> 24) & 0xff; \
(p)[5] = ((i) >> 16) & 0xff; \
(p)[6] = ((i) >> 8) & 0xff; \
(p)[7] = (i) & 0xff; \
} while(0)
int
sftp_put_uint64(struct sftp_output *o, UINT64 value)
{
UINT8 buf[8];
WRITE_UINT64(buf, value);
return PUT_DATA(o, buf);
}
int
sftp_put_string(struct sftp_output *o, UINT32 length, UINT8 *data)
{
return sftp_put_uint32(o, length)
&& sftp_put_data(o, length, data);
}
UINT8 *
sftp_put_reserve(struct sftp_output *o, UINT32 length)
{
UINT8 *result;
if (!sftp_check_output(o, length))
return NULL;
result = o->data + o->i;
o->i += length;
return result;
}
/* The first part of the buffer is always
*
* uint32 length
* uint8 msg
* uint32 id/version
*/
struct sftp_output *
sftp_make_output(FILE *f)
{
struct sftp_output *o = malloc(sizeof(struct sftp_output));
if (o)
{
o->f = f;
o->data = NULL;
o->size = 0;
o->i = 0;
}
return o;
}
void
sftp_set_msg(struct sftp_output *o, UINT8 msg)
{
o->msg = msg;
}
void
sftp_set_id(struct sftp_output *o, UINT32 id)
{
o->first = id;
}
int
sftp_write_packet(struct sftp_output *o)
{
UINT32 length = o->i + 5;
UINT8 buf[9];
WRITE_UINT32(buf, length);
buf[4] = o->msg;
WRITE_UINT32(buf + 5, o->first);
if (fwrite(buf, 1, 9, o->f) != 9)
return 0;
if (fwrite(o->data, 1, o->i, o->f) != o->i)
return 0;
o->i = 0;
/* FIXME: Flushing after each packet is sub-optimal. */
if (fflush(o->f))
return 0;
return 1;
}
#endif /* LSH */
/* General functions */
/* buffer.h
*
* Buffering for sftp.
*/
#ifndef SFTP_BUFFER_H_INCLUDED
#define SFTP_BUFFER_H_INCLUDED
#define LSH 1
#if LSH
#include "../lsh_types.h"
/* FIXME: Add to lsh_types.h */
#define UINT64 long long
#include <stdio.h>
struct sftp_input *
sftp_make_input(FILE *f);
/* Returns 1 of all was well, 0 on error, and -1 on EOF */
int
sftp_read_packet(struct sftp_input *i);
struct sftp_output *
sftp_make_output(FILE *f);
void
sftp_set_msg(struct sftp_output *o, UINT8 msg);
void
sftp_set_id(struct sftp_output *o, UINT32 id);
int
sftp_write_packet(struct sftp_output *o);
#elif OPENSSH
#else /* !OPENSSH */
# error Needs either LSH or OPENSSH
#endif /* !OPENSSH */
struct sftp_input;
struct sftp_output;
/* Input */
int
sftp_get_data(struct sftp_input *i, UINT32 length, UINT8 *data);
int
sftp_get_uint8(struct sftp_input *i, UINT8 *value);
int
sftp_get_uint32(struct sftp_input *i, UINT32 *value);
int
sftp_get_uint64(struct sftp_input *i, UINT64 *value);
UINT8 *
sftp_get_string(struct sftp_input *i, UINT32 *length);
void
sftp_free_string(UINT8 *data);
int
sftp_get_eod(struct sftp_input *i);
/* Output */
int
sftp_put_data(struct sftp_output *o, UINT32 length, const UINT8 *data);
int
sftp_put_uint8(struct sftp_output *o, UINT8 value);
int
sftp_put_uint32(struct sftp_output *o, UINT32 value);
int
sftp_put_uint64(struct sftp_output *o, UINT64 value);
int
sftp_put_string(struct sftp_output *o, UINT32 length, UINT8 *data);
UINT8 *
sftp_put_reserve(struct sftp_output *o, UINT32 length);
#endif /* SFTP_BUFFER_H_INCLUDED */
/* sftp-server.c
*
*/
#include "buffer.h"
#include "sftp.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#define SFTP_VERSION 3
#define FATAL(x) do { fputs("sftp-server: " x "\n", stderr); exit(EXIT_FAILURE); } while (0)
struct sftp_attrib
{
UINT32 flags;
UINT64 size;
UINT32 uid;
UINT32 gid;
UINT32 permissions;
/* NOTE: The representations of times is about to change. */
UINT32 atime;
UINT32 mtime;
};
static void
sftp_clear_attrib(struct sftp_attrib *a)
{
a->flags = 0;
a->size = 0;
a->uid = 0;
a->gid = 0;
a->permissions = 0;
a->atime = 0;
a->mtime = 0;
};
int
sftp_skip_extension(struct sftp_input *i)
{
UINT32 length;
UINT8 *data;
unsigned j;
/* Skip name and value*/
for (j = 0; j<2; j++)
{
if (!(data = sftp_get_string(i, &length)))
return 0;
sftp_free_string(data);
}
return 1;
}
int
sftp_get_attrib(struct sftp_input *i, struct sftp_attrib *a)
{
sftp_clear_attrib(a);
if (!sftp_get_uint32(i, &a->flags))
return 0;
if (a->flags & SSH_FILEXFER_ATTR_SIZE)
{
if (!sftp_get_uint64(i, &a->size))
return 0;
}
if (a->flags & SSH_FILEXFER_ATTR_UIDGID)
{
if (!sftp_get_uint32(i, &a->uid))
return 0;
if (!sftp_get_uint32(i, &a->gid))
return 0;
}
if (a->flags & SSH_FILEXFER_ATTR_PERMISSIONS)
{
if (!sftp_get_uint32(i, &a->permissions))
return 0;
}
if (a->flags & SSH_FILEXFER_ATTR_ACMODTIME)
{
if (!sftp_get_uint32(i, &a->atime))
return 0;
if (!sftp_get_uint32(i, &a->mtime))
return 0;
}
if (a->flags & SSH_FILEXFER_ATTR_EXTENDED)
{
UINT32 count;
UINT32 n;
if (!sftp_get_uint32(i, &count))
return 0;
/* Just skip the extensions */
for (n = 0; n < count; n++)
if (!sftp_skip_extension(i))
return 0;
}
return 1;
}
int
sftp_put_attrib(struct sftp_output *o, const struct sftp_attrib *a)
{
assert(!a->flags & SSH_FILEXFER_ATTR_EXTENDED);
if (!sftp_put_uint32(o, a->flags))
return 0;
if (a->flags & SSH_FILEXFER_ATTR_SIZE)
{
if (!sftp_put_uint64(o, a->size))
return 0;
}
if (a->flags & SSH_FILEXFER_ATTR_UIDGID)
{
if (!sftp_put_uint32(o, a->uid))
return 0;
if (!sftp_put_uint32(o, a->gid))
return 0;
}
if (a->flags & SSH_FILEXFER_ATTR_PERMISSIONS)
{
if (!sftp_put_uint32(o, a->permissions))
return 0;
}
if (a->flags & SSH_FILEXFER_ATTR_ACMODTIME)
{
if (!sftp_put_uint32(o, a->atime))
return 0;
if (!sftp_put_uint32(o, a->mtime))
return 0;
}
return 1;
}
#define SFTP_MAX_FDS 200
/* A handle is simply an fd */
#define handle_t UINT32
struct sftp_ctx
{
struct sftp_input *i;
struct sftp_output *o;
enum sftp_handle_type
{ HANDLE_UNUSED = 0, HANDLE_FILE, HANDLE_DIR } handles[SFTP_MAX_FDS];
};
void
sftp_init(struct sftp_ctx *ctx, FILE *in, FILE *out)
{
struct sftp_input *input;
struct sftp_output *output;
unsigned i;
if (!(input = sftp_make_input(stdin)))
FATAL("sftp_make_input failed");
if (!(output = sftp_make_output(stdout)))
FATAL("sftp_make_input failed");
ctx->i = input;
ctx->o = output;
for (i = 0; i < SFTP_MAX_FDS; i++)
ctx->handles[i] = HANDLE_UNUSED;
}
int
sftp_handle_used(struct sftp_ctx *ctx, handle_t handle)
{
return (handle < SFTP_MAX_FDS)
&& (ctx->handles[handle] != HANDLE_UNUSED);
}
void
sftp_register_handle(struct sftp_ctx *ctx,
UINT32 handle,
enum sftp_handle_type type)
{
assert(handle < SFTP_MAX_FDS);
assert(ctx->handles[handle] == HANDLE_UNUSED);
assert(type != HANDLE_UNUSED);
ctx->handles[handle] = type;
}