Commit 2226927f authored by Pontus Sköld's avatar Pontus Sköld Committed by Niels Möller
Browse files

* src/sftp/sftp-server.c: Merged code from Pontus Sköld.

(sftp_put_longname_mode): New function.
(sftp_put_longname): New function.
(sftp_put_filename): New function.
(getuser): New dummy function.
(getgroup): New dummy function.

Rev: src/sftp/sftp-server.c:1.3
parent 611a27a9
/* sftp-server.c /* sftp-server.c
* *
*/ * $Id$
*
* The server side of the sftp subsystem. */
/* lsh, an implementation of the ssh protocol
*
* Copyright (C) 2001 Niels Mller, Pontus Skld
*
* Also includes parts from GNU fileutils, Copyright by Free Software
* Foundation, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "buffer.h" #include "buffer.h"
#include "sftp.h" #include "sftp.h"
#include "filemode.h"
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
...@@ -15,145 +40,147 @@ ...@@ -15,145 +40,147 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#define SFTP_VERSION 3 #define SFTP_VERSION 3
#define FATAL(x) do { fputs("sftp-server: " x "\n", stderr); exit(EXIT_FAILURE); } while (0) #define FATAL(x) do { fputs("sftp-server: " x "\n", stderr); exit(EXIT_FAILURE); } while (0)
struct sftp_attrib void sftp_attrib_from_stat( struct stat *st, struct sftp_attrib* a)
{ {
UINT32 flags; a->permissions=st->st_mode;
UINT64 size; a->uid=st->st_uid;
UINT32 uid; a->gid=st->st_gid;
UINT32 gid; a->atime=st->st_atime;
UINT32 permissions; a->mtime=st->st_mtime;
a->size=st->st_size;
/* NOTE: The representations of times is about to change. */ a->flags= ( SSH_FILEXFER_ATTR_SIZE ||
UINT32 atime; SSH_FILEXFER_ATTR_UIDGID ||
UINT32 mtime; SSH_FILEXFER_ATTR_PERMISSIONS ||
}; SSH_FILEXFER_ATTR_ACMODTIME
);
}
static void static void
sftp_clear_attrib(struct sftp_attrib *a) sftp_put_longname_mode(struct sftp_output *o, struct stat *st)
{
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; /* A 10 character modestring and a space */
UINT8 *data; UINT8 modes[MODE_STRING_LENGTH];
unsigned j;
/* Skip name and value*/ filemodestring(st, modes);
for (j = 0; j<2; j++)
{
if (!(data = sftp_get_string(i, &length)))
return 0;
sftp_free_string(data); sftp_put_data(o, sizeof(modes), modes);
}
return 1;
} }
int /* FIXME: Replace these dummy functions with the functions from
sftp_get_attrib(struct sftp_input *i, struct sftp_attrib *a) * fileutil's lib/idcache.c. */
{ static const char *
sftp_clear_attrib(a); getuser(uid_t uid)
{ return NULL; }
if (!sftp_get_uint32(i, &a->flags)) static const char *
return 0; getgroup(gid_t gid)
{ return NULL; }
if (a->flags & SSH_FILEXFER_ATTR_SIZE) static void
{ sftp_put_longname(struct sftp_output *o,
if (!sftp_get_uint64(i, &a->size)) struct stat *st, UINT8* fname)
return 0; {
} /* NOTE: The current spec doesn't mandate utf8. */
/* Where to store the length. */
UINT32 length_index = sftp_put_reserve_length(o);
const char *user_name;
const char *group_name;
time_t now, when;
struct tm *when_local;
const char *time_format;
sftp_put_longname_mode(o, st);
sftp_put_printf(o, " %3u ", (unsigned) st->st_nlink);
user_name = getuser(st->st_uid);
if (user_name)
sftp_put_printf(o, "%-8.8s ", user_name);
else
sftp_put_printf(o, "%-8u ", (unsigned int) st->st_uid);
if (a->flags & SSH_FILEXFER_ATTR_UIDGID) group_name = getgroup(st->st_gid);
{ if (group_name)
if (!sftp_get_uint32(i, &a->uid)) sftp_put_printf(o, "%-8.8s ", group_name);
return 0; else
sftp_put_printf(o, "%-8u ", (unsigned) st->st_gid);
if (!sftp_get_uint32(i, &a->gid)) /* FIXME: How to deal with long long sizes? */
return 0; sftp_put_printf(o, "%8lu ", (unsigned long) st->st_size);
}
if (a->flags & SSH_FILEXFER_ATTR_PERMISSIONS) now = time(NULL);
{ when = st->st_mtime;
if (!sftp_get_uint32(i, &a->permissions))
return 0;
}
if (a->flags & SSH_FILEXFER_ATTR_ACMODTIME) when_local = localtime( &st->st_mtime );
{
if (!sftp_get_uint32(i, &a->atime))
return 0;
if (!sftp_get_uint32(i, &a->mtime)) if ( (now > when + 6L * 30L * 24L * 60L * 60L) /* Old. */
return 0; || (now < when - 60L * 60L) ) /* In the future. */
} /* The file is fairly old or in the future.
POSIX says the cutoff is 6 months old;
approximate this by 6*30 days.
Allow a 1 hour slop factor for what is considered "the future",
to allow for NFS server/client clock disagreement.
Show the year instead of the time of day. */
time_format = "%b %e %Y";
else
time_format = "%b %e %H:%M";
if (a->flags & SSH_FILEXFER_ATTR_EXTENDED) sftp_put_strftime(o, 12, time_format, when_local);
{
UINT32 count;
UINT32 n;
if (!sftp_get_uint32(i, &count)) sftp_put_printf(o, " %s", fname);
return 0;
/* Just skip the extensions */ sftp_put_final_length(o, length_index);
for (n = 0; n < count; n++)
if (!sftp_skip_extension(i))
return 0;
}
return 1;
} }
void static void
sftp_put_attrib(struct sftp_output *o, const struct sftp_attrib *a) sftp_put_filename(struct sftp_output *o,
struct stat *st,
const char *name)
{ {
assert(!a->flags & SSH_FILEXFER_ATTR_EXTENDED); struct sftp_attrib a;
sftp_put_uint32(o, a->flags);
if (a->flags & SSH_FILEXFER_ATTR_SIZE) sftp_attrib_from_stat( st, &a);
sftp_put_uint64(o, a->size); sftp_put_string(o, strlen(name), name);
sftp_put_longname(o, st, name);
sftp_put_attrib(o, &a);
}
if (a->flags & SSH_FILEXFER_ATTR_UIDGID) #define SFTP_MAX_HANDLES 200
{
sftp_put_uint32(o, a->uid);
sftp_put_uint32(o, a->gid);
}
if (a->flags & SSH_FILEXFER_ATTR_PERMISSIONS) struct sftp_handle
sftp_put_uint32(o, a->permissions); {
enum sftp_handle_type
{ HANDLE_UNUSED = 0, HANDLE_FILE, HANDLE_DIR } type;
if (a->flags & SSH_FILEXFER_ATTR_ACMODTIME) union
{ {
sftp_put_uint32(o, a->atime); int fd;
sftp_put_uint32(o, a->mtime); DIR *dir;
} } u;
} };
#define SFTP_MAX_FDS 200
#if 0
/* A handle is simply an fd */ /* A handle is simply an fd */
#define handle_t UINT32 #define handle_t UINT32
#else
#define handle_t struct sftp_handle *
#endif
struct sftp_ctx struct sftp_ctx
{ {
struct sftp_input *i; struct sftp_input *i;
struct sftp_output *o; struct sftp_output *o;
enum sftp_handle_type struct sftp_handle handles[SFTP_MAX_HANDLES];
{ HANDLE_UNUSED = 0, HANDLE_FILE, HANDLE_DIR } handles[SFTP_MAX_FDS];
}; };
void void
...@@ -172,15 +199,15 @@ sftp_init(struct sftp_ctx *ctx, FILE *in, FILE *out) ...@@ -172,15 +199,15 @@ sftp_init(struct sftp_ctx *ctx, FILE *in, FILE *out)
ctx->i = input; ctx->i = input;
ctx->o = output; ctx->o = output;
for (i = 0; i < SFTP_MAX_FDS; i++) for (i = 0; i < SFTP_MAX_HANDLES; i++)
ctx->handles[i] = HANDLE_UNUSED; ctx->handles[i].type = HANDLE_UNUSED;
} }
int int
sftp_handle_used(struct sftp_ctx *ctx, handle_t handle) sftp_handle_used(struct sftp_ctx *ctx, handle_t handle)
{ {
return (handle < SFTP_MAX_FDS) return (handle < SFTP_MAX_HANDLES)
&& (ctx->handles[handle] != HANDLE_UNUSED); && (ctx->handles[handle].type != HANDLE_UNUSED);
} }
void void
...@@ -188,8 +215,8 @@ sftp_register_handle(struct sftp_ctx *ctx, ...@@ -188,8 +215,8 @@ sftp_register_handle(struct sftp_ctx *ctx,
UINT32 handle, UINT32 handle,
enum sftp_handle_type type) enum sftp_handle_type type)
{ {
assert(handle < SFTP_MAX_FDS); assert(handle < SFTP_MAX_HANDLES);
assert(ctx->handles[handle] == HANDLE_UNUSED); assert(ctx->handles[handle].type == HANDLE_UNUSED);
assert(type != HANDLE_UNUSED); assert(type != HANDLE_UNUSED);
ctx->handles[handle] = type; ctx->handles[handle] = type;
...@@ -271,6 +298,397 @@ sftp_check_filename(UINT32 length, const UINT8 *data) ...@@ -271,6 +298,397 @@ sftp_check_filename(UINT32 length, const UINT8 *data)
return !memchr(data, 0, length); return !memchr(data, 0, length);
} }
static int
sftp_process_opendir(struct sftp_ctx *ctx)
{
UINT32 length;
UINT8 *name;
DIR* dirhandle;
if ( ! (name = sftp_get_string(ctx->i, &length))
)
return sftp_bad_message(ctx);
if( (! sftp_check_filename(length, name)))
{
sftp_free_string(name);
return sftp_send_status(ctx, SSH_FX_FAILURE);
}
/* Fixme; Perhaps we should have a sftp_mangle_fname? If not, we
have to handle an empty filename here */
dirhandle=opendir(length ? name : ".");
sftp_free_string(name);
if ( !dirhandle )
return sftp_send_errno(ctx);
/* Open successful */
/* Fixme; we need to redo how handles work, perhaps a struct
consisting of the type, an int (for an fd) and a DIR* (for a
directory). I will look into this later (or we could skip using
opendir/readdir/closedir, but I think that's not better */
sftp_register_handle(ctx, dirhandle, HANDLE_DIR);
sftp_put_handle(ctx, dirhandle);
}
static int
sftp_process_readdir(struct sftp_ctx *ctx)
{
handle_t handle;
struct dirent* direntry;
struct stat st;
if ( !sftp_get_handle(ctx->i, &handle) ||
ctx->handles[handle] != HANDLE_DIR
)
return sftp_bad_message(ctx);
direntry=readdir(handle);
/* Fixme; we need to redo how handles work, perhaps a struct
consisting of the type, an int (for an fd) and a DIR* (for a
directory). I will look into this later (or we could skip using
opendir/readdir/closedir, but I think that's not better */
if ( !direntry )
return (errno ? sftp_send_errno(ctx)
: sftp_send_status(ctx, SSH_FX_EOF));
/* Fixme; concat name */
if( lstat(direntry->d_name, &st ) )
return sftp_send_errno(ctx);
/* Fixme; we don't have to, but maybe we should be nice and pass
several at once? It might improve performance quite a lot (or it
might not)
*/
/* Use count == 1 for now. */
sftp_put_uint32(ctx->o, 1);
sftp_put_filename(ctx->o, &st, direntry->d_name);
sftp_set_msg(ctx->o, SSH_FXP_NAME );
return 1;
}
static int
sftp_process_stat(struct sftp_ctx *ctx)
{
struct stat st;
struct sftp_attrib a;
UINT32 length;
UINT8 *name;
if ( ! (name = sftp_get_string(ctx->i, &length))
)
return sftp_bad_message(ctx);
if( (! sftp_check_filename(length, name))
)
return sftp_send_status(ctx, SSH_FX_FAILURE);
/* Fixme; Perhaps we should have a sftp_mangle_fname ? */
/* Fixme; concat name */
if ( stat(name, &st ) )
return sftp_send_errno(ctx);
sftp_attrib_from_stat( &st, &a);
sftp_set_msg( ctx->o, SSH_FXP_ATTRS );
sftp_put_attrib( ctx->o, &a);
return 1;
}
static int
sftp_process_lstat(struct sftp_ctx *ctx)
{
struct stat st;
struct sftp_attrib a;
UINT32 length;
UINT8 *name;
if ( ! (name = sftp_get_string(ctx->i, &length))
)
return sftp_bad_message(ctx);
if( (! sftp_check_filename(length, name))
)
return sftp_send_status(ctx, SSH_FX_FAILURE);
/* Fixme; Perhaps we should have a sftp_mangle_fname ? */
/* Fixme; concat name */
if ( lstat(name, &st ) )
return sftp_send_errno(ctx);
sftp_attrib_from_stat( &st, &a );
sftp_set_msg( ctx->o, SSH_FXP_ATTRS );
sftp_put_attrib( ctx->o, &a);
return 1;
}
static int
sftp_process_fstat(struct sftp_ctx *ctx)
{
struct stat st;
struct sftp_attrib a;
handle_t handle;
if ( sftp_get_handle(ctx->i, &handle) )
if ( ctx->handles[handle] == HANDLE_FILE )
{
if ( fstat(handle, &st ) )
return sftp_send_errno(ctx);
sftp_attrib_from_stat(&st,&a);
sftp_set_msg( ctx->o, SSH_FXP_ATTRS );
sftp_put_attrib( ctx->o, &a);
return 1;
}
return sftp_bad_message(ctx);
}
static int
sftp_process_fsetstat(struct sftp_ctx *ctx)
{
struct sftp_attrib a;
handle_t handle;
if ( sftp_get_handle(ctx->i, &handle) &&
sftp_get_attrib(ctx->i, &a)
)
if ( ! (ctx->handles[handle] == HANDLE_FILE ))
return sftp_bad_message(ctx);
/* Fixme; set stat */
if ( a.flags & SSH_FILEXFER_ATTR_UIDGID )
if ( fchown( handle, a.uid, a.gid ) )
return sftp_send_errno(ctx);
if ( a.flags & SSH_FILEXFER_ATTR_SIZE )
if( ftruncate( handle, a.size ) )
return sftp_send_errno(ctx);
if ( a.flags & SSH_FILEXFER_ATTR_PERMISSIONS )
if( fchmod( handle, a.permissions ) )
/* Fixme; Perhaps we should mask it */
return sftp_send_errno(ctx);
if ( a.flags & SSH_FILEXFER_ATTR_EXTENDED ||
a.flags & SSH_FILEXFER_ATTR_ACMODTIME ) /* Fixme; how do we? */
return sftp_send_status(ctx, SSH_FX_OP_UNSUPPORTED );
return sftp_send_status(ctx, SSH_FX_OK);
}
static int
sftp_process_setstat(struct sftp_ctx *ctx)
{
struct sftp_attrib a;
UINT32 length;
UINT8 *name;
if ( (! (name = sftp_get_string(ctx->i, &length))) ||
(!sftp_get_attrib(ctx->i, &a))
)
return sftp_bad_message(ctx);
if( (! sftp_check_filename(length, name))
)
return sftp_send_status(ctx, SSH_FX_FAILURE);
/* Fixme; Perhaps we should have a sftp_mangle_fname ? */
if ( a.flags & SSH_FILEXFER_ATTR_UIDGID )
if ( chown( name, a.uid, a.gid ) )
return sftp_send_errno(ctx);
if ( a.flags & SSH_FILEXFER_ATTR_SIZE )
if( truncate( name, a.size ) )
return sftp_send_errno(ctx);
if ( a.flags & SSH_FILEXFER_ATTR_PERMISSIONS )
if( chmod( name, a.permissions ) ) /* Fixme; Perhaps we should mask it */
return sftp_send_errno(ctx);
if ( a.flags & SSH_FILEXFER_ATTR_EXTENDED ||
a.flags & SSH_FILEXFER_ATTR_ACMODTIME ) /* Fixme; how do we? */
return sftp_send_status(ctx, SSH_FX_OP_UNSUPPORTED );
return sftp_send_status(ctx, SSH_FX_OK);
}
static int
sftp_process_remove(struct sftp_ctx *ctx)
{
UINT32 length;
UINT8 *name;
if ( ! (name = sftp_get_string(ctx->i, &length))
)
return sftp_bad_message(ctx);
if( (! sftp_check_filename(length, name))
)
return sftp_send_status(ctx, SSH_FX_FAILURE);
/* Fixme; Perhaps we should have a sftp_mangle_fname ? */
if( ! unlink(name) )
return sftp_send_errno(ctx);
else
return sftp_send_status(ctx, SSH_FX_OK);
}
static int
sftp_process_mkdir(struct sftp_ctx *ctx)
{
UINT32 length;
UINT8 *name;
if ( ! (name = sftp_get_string(ctx->i, &length))
)
return sftp_bad_message(ctx);