sftp-server.c revision 92555
165668Skris/*
292555Sdes * Copyright (c) 2000, 2001, 2002 Markus Friedl.  All rights reserved.
365668Skris *
465668Skris * Redistribution and use in source and binary forms, with or without
565668Skris * modification, are permitted provided that the following conditions
665668Skris * are met:
765668Skris * 1. Redistributions of source code must retain the above copyright
865668Skris *    notice, this list of conditions and the following disclaimer.
965668Skris * 2. Redistributions in binary form must reproduce the above copyright
1065668Skris *    notice, this list of conditions and the following disclaimer in the
1165668Skris *    documentation and/or other materials provided with the distribution.
1265668Skris *
1365668Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1465668Skris * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1565668Skris * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1665668Skris * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1765668Skris * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
1865668Skris * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
1965668Skris * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2065668Skris * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2165668Skris * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2265668Skris * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2365668Skris */
2465668Skris#include "includes.h"
2592555SdesRCSID("$OpenBSD: sftp-server.c,v 1.33 2002/02/13 00:28:13 markus Exp $");
2665668Skris
2765668Skris#include "buffer.h"
2865668Skris#include "bufaux.h"
2965668Skris#include "getput.h"
3076259Sgreen#include "log.h"
3165668Skris#include "xmalloc.h"
3265668Skris
3376259Sgreen#include "sftp.h"
3476259Sgreen#include "sftp-common.h"
3565668Skris
3665668Skris/* helper */
3776259Sgreen#define get_int64()			buffer_get_int64(&iqueue);
3865668Skris#define get_int()			buffer_get_int(&iqueue);
3965668Skris#define get_string(lenp)		buffer_get_string(&iqueue, lenp);
4076259Sgreen#define TRACE				debug
4165668Skris
4265668Skris/* input and output queue */
4365668SkrisBuffer iqueue;
4465668SkrisBuffer oqueue;
4565668Skris
4676259Sgreen/* Version of client */
4776259Sgreenint version;
4876259Sgreen
4965668Skris/* portable attibutes, etc. */
5065668Skris
5165668Skristypedef struct Stat Stat;
5265668Skris
5376259Sgreenstruct Stat {
5465668Skris	char *name;
5565668Skris	char *long_name;
5665668Skris	Attrib attrib;
5765668Skris};
5865668Skris
5992555Sdesstatic int
6065668Skriserrno_to_portable(int unixerrno)
6165668Skris{
6265668Skris	int ret = 0;
6376259Sgreen
6465668Skris	switch (unixerrno) {
6565668Skris	case 0:
6676259Sgreen		ret = SSH2_FX_OK;
6765668Skris		break;
6865668Skris	case ENOENT:
6965668Skris	case ENOTDIR:
7065668Skris	case EBADF:
7165668Skris	case ELOOP:
7276259Sgreen		ret = SSH2_FX_NO_SUCH_FILE;
7365668Skris		break;
7465668Skris	case EPERM:
7565668Skris	case EACCES:
7665668Skris	case EFAULT:
7776259Sgreen		ret = SSH2_FX_PERMISSION_DENIED;
7865668Skris		break;
7965668Skris	case ENAMETOOLONG:
8065668Skris	case EINVAL:
8176259Sgreen		ret = SSH2_FX_BAD_MESSAGE;
8265668Skris		break;
8365668Skris	default:
8476259Sgreen		ret = SSH2_FX_FAILURE;
8565668Skris		break;
8665668Skris	}
8765668Skris	return ret;
8865668Skris}
8965668Skris
9092555Sdesstatic int
9165668Skrisflags_from_portable(int pflags)
9265668Skris{
9365668Skris	int flags = 0;
9476259Sgreen
9576259Sgreen	if ((pflags & SSH2_FXF_READ) &&
9676259Sgreen	    (pflags & SSH2_FXF_WRITE)) {
9765668Skris		flags = O_RDWR;
9876259Sgreen	} else if (pflags & SSH2_FXF_READ) {
9965668Skris		flags = O_RDONLY;
10076259Sgreen	} else if (pflags & SSH2_FXF_WRITE) {
10165668Skris		flags = O_WRONLY;
10265668Skris	}
10376259Sgreen	if (pflags & SSH2_FXF_CREAT)
10465668Skris		flags |= O_CREAT;
10576259Sgreen	if (pflags & SSH2_FXF_TRUNC)
10665668Skris		flags |= O_TRUNC;
10776259Sgreen	if (pflags & SSH2_FXF_EXCL)
10865668Skris		flags |= O_EXCL;
10965668Skris	return flags;
11065668Skris}
11165668Skris
11292555Sdesstatic Attrib *
11365668Skrisget_attrib(void)
11465668Skris{
11565668Skris	return decode_attrib(&iqueue);
11665668Skris}
11765668Skris
11865668Skris/* handle handles */
11965668Skris
12065668Skristypedef struct Handle Handle;
12165668Skrisstruct Handle {
12265668Skris	int use;
12365668Skris	DIR *dirp;
12465668Skris	int fd;
12565668Skris	char *name;
12665668Skris};
12776259Sgreen
12865668Skrisenum {
12965668Skris	HANDLE_UNUSED,
13065668Skris	HANDLE_DIR,
13165668Skris	HANDLE_FILE
13265668Skris};
13376259Sgreen
13465668SkrisHandle	handles[100];
13565668Skris
13692555Sdesstatic void
13765668Skrishandle_init(void)
13865668Skris{
13965668Skris	int i;
14076259Sgreen
14192555Sdes	for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
14265668Skris		handles[i].use = HANDLE_UNUSED;
14365668Skris}
14465668Skris
14592555Sdesstatic int
14665668Skrishandle_new(int use, char *name, int fd, DIR *dirp)
14765668Skris{
14865668Skris	int i;
14976259Sgreen
15092555Sdes	for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
15165668Skris		if (handles[i].use == HANDLE_UNUSED) {
15265668Skris			handles[i].use = use;
15365668Skris			handles[i].dirp = dirp;
15465668Skris			handles[i].fd = fd;
15565668Skris			handles[i].name = name;
15665668Skris			return i;
15765668Skris		}
15865668Skris	}
15965668Skris	return -1;
16065668Skris}
16165668Skris
16292555Sdesstatic int
16365668Skrishandle_is_ok(int i, int type)
16465668Skris{
16576259Sgreen	return i >= 0 && i < sizeof(handles)/sizeof(Handle) &&
16676259Sgreen	    handles[i].use == type;
16765668Skris}
16865668Skris
16992555Sdesstatic int
17065668Skrishandle_to_string(int handle, char **stringp, int *hlenp)
17165668Skris{
17265668Skris	if (stringp == NULL || hlenp == NULL)
17365668Skris		return -1;
17476259Sgreen	*stringp = xmalloc(sizeof(int32_t));
17576259Sgreen	PUT_32BIT(*stringp, handle);
17676259Sgreen	*hlenp = sizeof(int32_t);
17765668Skris	return 0;
17865668Skris}
17965668Skris
18092555Sdesstatic int
18165668Skrishandle_from_string(char *handle, u_int hlen)
18265668Skris{
18376259Sgreen	int val;
18476259Sgreen
18576259Sgreen	if (hlen != sizeof(int32_t))
18665668Skris		return -1;
18776259Sgreen	val = GET_32BIT(handle);
18865668Skris	if (handle_is_ok(val, HANDLE_FILE) ||
18965668Skris	    handle_is_ok(val, HANDLE_DIR))
19065668Skris		return val;
19165668Skris	return -1;
19265668Skris}
19365668Skris
19492555Sdesstatic char *
19565668Skrishandle_to_name(int handle)
19665668Skris{
19765668Skris	if (handle_is_ok(handle, HANDLE_DIR)||
19865668Skris	    handle_is_ok(handle, HANDLE_FILE))
19965668Skris		return handles[handle].name;
20065668Skris	return NULL;
20165668Skris}
20265668Skris
20392555Sdesstatic DIR *
20465668Skrishandle_to_dir(int handle)
20565668Skris{
20665668Skris	if (handle_is_ok(handle, HANDLE_DIR))
20765668Skris		return handles[handle].dirp;
20865668Skris	return NULL;
20965668Skris}
21065668Skris
21192555Sdesstatic int
21265668Skrishandle_to_fd(int handle)
21365668Skris{
21476259Sgreen	if (handle_is_ok(handle, HANDLE_FILE))
21565668Skris		return handles[handle].fd;
21665668Skris	return -1;
21765668Skris}
21865668Skris
21992555Sdesstatic int
22065668Skrishandle_close(int handle)
22165668Skris{
22265668Skris	int ret = -1;
22376259Sgreen
22465668Skris	if (handle_is_ok(handle, HANDLE_FILE)) {
22565668Skris		ret = close(handles[handle].fd);
22665668Skris		handles[handle].use = HANDLE_UNUSED;
22765668Skris	} else if (handle_is_ok(handle, HANDLE_DIR)) {
22865668Skris		ret = closedir(handles[handle].dirp);
22965668Skris		handles[handle].use = HANDLE_UNUSED;
23065668Skris	} else {
23165668Skris		errno = ENOENT;
23265668Skris	}
23365668Skris	return ret;
23465668Skris}
23565668Skris
23692555Sdesstatic int
23765668Skrisget_handle(void)
23865668Skris{
23965668Skris	char *handle;
24076259Sgreen	int val = -1;
24165668Skris	u_int hlen;
24276259Sgreen
24365668Skris	handle = get_string(&hlen);
24476259Sgreen	if (hlen < 256)
24576259Sgreen		val = handle_from_string(handle, hlen);
24665668Skris	xfree(handle);
24765668Skris	return val;
24865668Skris}
24965668Skris
25065668Skris/* send replies */
25165668Skris
25292555Sdesstatic void
25365668Skrissend_msg(Buffer *m)
25465668Skris{
25565668Skris	int mlen = buffer_len(m);
25676259Sgreen
25765668Skris	buffer_put_int(&oqueue, mlen);
25865668Skris	buffer_append(&oqueue, buffer_ptr(m), mlen);
25965668Skris	buffer_consume(m, mlen);
26065668Skris}
26165668Skris
26292555Sdesstatic void
26365668Skrissend_status(u_int32_t id, u_int32_t error)
26465668Skris{
26565668Skris	Buffer msg;
26676259Sgreen	const char *status_messages[] = {
26776259Sgreen		"Success",			/* SSH_FX_OK */
26876259Sgreen		"End of file",			/* SSH_FX_EOF */
26976259Sgreen		"No such file",			/* SSH_FX_NO_SUCH_FILE */
27076259Sgreen		"Permission denied",		/* SSH_FX_PERMISSION_DENIED */
27176259Sgreen		"Failure",			/* SSH_FX_FAILURE */
27276259Sgreen		"Bad message",			/* SSH_FX_BAD_MESSAGE */
27376259Sgreen		"No connection",		/* SSH_FX_NO_CONNECTION */
27476259Sgreen		"Connection lost",		/* SSH_FX_CONNECTION_LOST */
27576259Sgreen		"Operation unsupported",	/* SSH_FX_OP_UNSUPPORTED */
27676259Sgreen		"Unknown error"			/* Others */
27776259Sgreen	};
27876259Sgreen
27965668Skris	TRACE("sent status id %d error %d", id, error);
28065668Skris	buffer_init(&msg);
28176259Sgreen	buffer_put_char(&msg, SSH2_FXP_STATUS);
28265668Skris	buffer_put_int(&msg, id);
28365668Skris	buffer_put_int(&msg, error);
28476259Sgreen	if (version >= 3) {
28576259Sgreen		buffer_put_cstring(&msg,
28676259Sgreen		    status_messages[MIN(error,SSH2_FX_MAX)]);
28776259Sgreen		buffer_put_cstring(&msg, "");
28876259Sgreen	}
28965668Skris	send_msg(&msg);
29065668Skris	buffer_free(&msg);
29165668Skris}
29292555Sdesstatic void
29365668Skrissend_data_or_handle(char type, u_int32_t id, char *data, int dlen)
29465668Skris{
29565668Skris	Buffer msg;
29676259Sgreen
29765668Skris	buffer_init(&msg);
29865668Skris	buffer_put_char(&msg, type);
29965668Skris	buffer_put_int(&msg, id);
30065668Skris	buffer_put_string(&msg, data, dlen);
30165668Skris	send_msg(&msg);
30265668Skris	buffer_free(&msg);
30365668Skris}
30465668Skris
30592555Sdesstatic void
30665668Skrissend_data(u_int32_t id, char *data, int dlen)
30765668Skris{
30865668Skris	TRACE("sent data id %d len %d", id, dlen);
30976259Sgreen	send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
31065668Skris}
31165668Skris
31292555Sdesstatic void
31365668Skrissend_handle(u_int32_t id, int handle)
31465668Skris{
31565668Skris	char *string;
31665668Skris	int hlen;
31776259Sgreen
31865668Skris	handle_to_string(handle, &string, &hlen);
31965668Skris	TRACE("sent handle id %d handle %d", id, handle);
32076259Sgreen	send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
32165668Skris	xfree(string);
32265668Skris}
32365668Skris
32492555Sdesstatic void
32565668Skrissend_names(u_int32_t id, int count, Stat *stats)
32665668Skris{
32765668Skris	Buffer msg;
32865668Skris	int i;
32976259Sgreen
33065668Skris	buffer_init(&msg);
33176259Sgreen	buffer_put_char(&msg, SSH2_FXP_NAME);
33265668Skris	buffer_put_int(&msg, id);
33365668Skris	buffer_put_int(&msg, count);
33465668Skris	TRACE("sent names id %d count %d", id, count);
33565668Skris	for (i = 0; i < count; i++) {
33665668Skris		buffer_put_cstring(&msg, stats[i].name);
33765668Skris		buffer_put_cstring(&msg, stats[i].long_name);
33865668Skris		encode_attrib(&msg, &stats[i].attrib);
33965668Skris	}
34065668Skris	send_msg(&msg);
34165668Skris	buffer_free(&msg);
34265668Skris}
34365668Skris
34492555Sdesstatic void
34565668Skrissend_attrib(u_int32_t id, Attrib *a)
34665668Skris{
34765668Skris	Buffer msg;
34876259Sgreen
34965668Skris	TRACE("sent attrib id %d have 0x%x", id, a->flags);
35065668Skris	buffer_init(&msg);
35176259Sgreen	buffer_put_char(&msg, SSH2_FXP_ATTRS);
35265668Skris	buffer_put_int(&msg, id);
35365668Skris	encode_attrib(&msg, a);
35465668Skris	send_msg(&msg);
35565668Skris	buffer_free(&msg);
35665668Skris}
35765668Skris
35865668Skris/* parse incoming */
35965668Skris
36092555Sdesstatic void
36165668Skrisprocess_init(void)
36265668Skris{
36365668Skris	Buffer msg;
36465668Skris
36576259Sgreen	version = buffer_get_int(&iqueue);
36665668Skris	TRACE("client version %d", version);
36765668Skris	buffer_init(&msg);
36876259Sgreen	buffer_put_char(&msg, SSH2_FXP_VERSION);
36976259Sgreen	buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
37065668Skris	send_msg(&msg);
37165668Skris	buffer_free(&msg);
37265668Skris}
37365668Skris
37492555Sdesstatic void
37565668Skrisprocess_open(void)
37665668Skris{
37765668Skris	u_int32_t id, pflags;
37865668Skris	Attrib *a;
37965668Skris	char *name;
38076259Sgreen	int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
38165668Skris
38265668Skris	id = get_int();
38365668Skris	name = get_string(NULL);
38476259Sgreen	pflags = get_int();		/* portable flags */
38565668Skris	a = get_attrib();
38665668Skris	flags = flags_from_portable(pflags);
38776259Sgreen	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
38865668Skris	TRACE("open id %d name %s flags %d mode 0%o", id, name, pflags, mode);
38965668Skris	fd = open(name, flags, mode);
39065668Skris	if (fd < 0) {
39165668Skris		status = errno_to_portable(errno);
39265668Skris	} else {
39365668Skris		handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL);
39465668Skris		if (handle < 0) {
39565668Skris			close(fd);
39665668Skris		} else {
39765668Skris			send_handle(id, handle);
39876259Sgreen			status = SSH2_FX_OK;
39965668Skris		}
40065668Skris	}
40176259Sgreen	if (status != SSH2_FX_OK)
40265668Skris		send_status(id, status);
40365668Skris	xfree(name);
40465668Skris}
40565668Skris
40692555Sdesstatic void
40765668Skrisprocess_close(void)
40865668Skris{
40965668Skris	u_int32_t id;
41076259Sgreen	int handle, ret, status = SSH2_FX_FAILURE;
41165668Skris
41265668Skris	id = get_int();
41365668Skris	handle = get_handle();
41465668Skris	TRACE("close id %d handle %d", id, handle);
41565668Skris	ret = handle_close(handle);
41676259Sgreen	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
41765668Skris	send_status(id, status);
41865668Skris}
41965668Skris
42092555Sdesstatic void
42165668Skrisprocess_read(void)
42265668Skris{
42365668Skris	char buf[64*1024];
42476259Sgreen	u_int32_t id, len;
42576259Sgreen	int handle, fd, ret, status = SSH2_FX_FAILURE;
42665668Skris	u_int64_t off;
42765668Skris
42865668Skris	id = get_int();
42965668Skris	handle = get_handle();
43076259Sgreen	off = get_int64();
43165668Skris	len = get_int();
43265668Skris
43376259Sgreen	TRACE("read id %d handle %d off %llu len %d", id, handle,
43476259Sgreen	    (unsigned long long)off, len);
43565668Skris	if (len > sizeof buf) {
43665668Skris		len = sizeof buf;
43765668Skris		log("read change len %d", len);
43865668Skris	}
43965668Skris	fd = handle_to_fd(handle);
44065668Skris	if (fd >= 0) {
44165668Skris		if (lseek(fd, off, SEEK_SET) < 0) {
44265668Skris			error("process_read: seek failed");
44365668Skris			status = errno_to_portable(errno);
44465668Skris		} else {
44565668Skris			ret = read(fd, buf, len);
44665668Skris			if (ret < 0) {
44765668Skris				status = errno_to_portable(errno);
44865668Skris			} else if (ret == 0) {
44976259Sgreen				status = SSH2_FX_EOF;
45065668Skris			} else {
45165668Skris				send_data(id, buf, ret);
45276259Sgreen				status = SSH2_FX_OK;
45365668Skris			}
45465668Skris		}
45565668Skris	}
45676259Sgreen	if (status != SSH2_FX_OK)
45765668Skris		send_status(id, status);
45865668Skris}
45965668Skris
46092555Sdesstatic void
46165668Skrisprocess_write(void)
46265668Skris{
46376259Sgreen	u_int32_t id;
46465668Skris	u_int64_t off;
46565668Skris	u_int len;
46676259Sgreen	int handle, fd, ret, status = SSH2_FX_FAILURE;
46765668Skris	char *data;
46865668Skris
46965668Skris	id = get_int();
47065668Skris	handle = get_handle();
47176259Sgreen	off = get_int64();
47265668Skris	data = get_string(&len);
47365668Skris
47476259Sgreen	TRACE("write id %d handle %d off %llu len %d", id, handle,
47576259Sgreen	    (unsigned long long)off, len);
47665668Skris	fd = handle_to_fd(handle);
47765668Skris	if (fd >= 0) {
47865668Skris		if (lseek(fd, off, SEEK_SET) < 0) {
47965668Skris			status = errno_to_portable(errno);
48065668Skris			error("process_write: seek failed");
48165668Skris		} else {
48265668Skris/* XXX ATOMICIO ? */
48365668Skris			ret = write(fd, data, len);
48465668Skris			if (ret == -1) {
48565668Skris				error("process_write: write failed");
48665668Skris				status = errno_to_portable(errno);
48765668Skris			} else if (ret == len) {
48876259Sgreen				status = SSH2_FX_OK;
48965668Skris			} else {
49065668Skris				log("nothing at all written");
49165668Skris			}
49265668Skris		}
49365668Skris	}
49465668Skris	send_status(id, status);
49565668Skris	xfree(data);
49665668Skris}
49765668Skris
49892555Sdesstatic void
49965668Skrisprocess_do_stat(int do_lstat)
50065668Skris{
50176259Sgreen	Attrib a;
50265668Skris	struct stat st;
50365668Skris	u_int32_t id;
50465668Skris	char *name;
50576259Sgreen	int ret, status = SSH2_FX_FAILURE;
50665668Skris
50765668Skris	id = get_int();
50865668Skris	name = get_string(NULL);
50965668Skris	TRACE("%sstat id %d name %s", do_lstat ? "l" : "", id, name);
51065668Skris	ret = do_lstat ? lstat(name, &st) : stat(name, &st);
51165668Skris	if (ret < 0) {
51265668Skris		status = errno_to_portable(errno);
51365668Skris	} else {
51476259Sgreen		stat_to_attrib(&st, &a);
51576259Sgreen		send_attrib(id, &a);
51676259Sgreen		status = SSH2_FX_OK;
51765668Skris	}
51876259Sgreen	if (status != SSH2_FX_OK)
51965668Skris		send_status(id, status);
52065668Skris	xfree(name);
52165668Skris}
52265668Skris
52392555Sdesstatic void
52465668Skrisprocess_stat(void)
52565668Skris{
52665668Skris	process_do_stat(0);
52765668Skris}
52865668Skris
52992555Sdesstatic void
53065668Skrisprocess_lstat(void)
53165668Skris{
53265668Skris	process_do_stat(1);
53365668Skris}
53465668Skris
53592555Sdesstatic void
53665668Skrisprocess_fstat(void)
53765668Skris{
53876259Sgreen	Attrib a;
53965668Skris	struct stat st;
54065668Skris	u_int32_t id;
54176259Sgreen	int fd, ret, handle, status = SSH2_FX_FAILURE;
54265668Skris
54365668Skris	id = get_int();
54465668Skris	handle = get_handle();
54565668Skris	TRACE("fstat id %d handle %d", id, handle);
54665668Skris	fd = handle_to_fd(handle);
54765668Skris	if (fd  >= 0) {
54865668Skris		ret = fstat(fd, &st);
54965668Skris		if (ret < 0) {
55065668Skris			status = errno_to_portable(errno);
55165668Skris		} else {
55276259Sgreen			stat_to_attrib(&st, &a);
55376259Sgreen			send_attrib(id, &a);
55476259Sgreen			status = SSH2_FX_OK;
55565668Skris		}
55665668Skris	}
55776259Sgreen	if (status != SSH2_FX_OK)
55865668Skris		send_status(id, status);
55965668Skris}
56065668Skris
56192555Sdesstatic struct timeval *
56265668Skrisattrib_to_tv(Attrib *a)
56365668Skris{
56465668Skris	static struct timeval tv[2];
56576259Sgreen
56665668Skris	tv[0].tv_sec = a->atime;
56765668Skris	tv[0].tv_usec = 0;
56865668Skris	tv[1].tv_sec = a->mtime;
56965668Skris	tv[1].tv_usec = 0;
57065668Skris	return tv;
57165668Skris}
57265668Skris
57392555Sdesstatic void
57465668Skrisprocess_setstat(void)
57565668Skris{
57665668Skris	Attrib *a;
57765668Skris	u_int32_t id;
57865668Skris	char *name;
57965668Skris	int ret;
58076259Sgreen	int status = SSH2_FX_OK;
58165668Skris
58265668Skris	id = get_int();
58365668Skris	name = get_string(NULL);
58465668Skris	a = get_attrib();
58565668Skris	TRACE("setstat id %d name %s", id, name);
58692555Sdes	if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
58792555Sdes		ret = truncate(name, a->size);
58892555Sdes		if (ret == -1)
58992555Sdes			status = errno_to_portable(errno);
59092555Sdes	}
59176259Sgreen	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
59265668Skris		ret = chmod(name, a->perm & 0777);
59365668Skris		if (ret == -1)
59465668Skris			status = errno_to_portable(errno);
59565668Skris	}
59676259Sgreen	if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
59765668Skris		ret = utimes(name, attrib_to_tv(a));
59865668Skris		if (ret == -1)
59965668Skris			status = errno_to_portable(errno);
60065668Skris	}
60176259Sgreen	if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
60276259Sgreen		ret = chown(name, a->uid, a->gid);
60376259Sgreen		if (ret == -1)
60476259Sgreen			status = errno_to_portable(errno);
60576259Sgreen	}
60665668Skris	send_status(id, status);
60765668Skris	xfree(name);
60865668Skris}
60965668Skris
61092555Sdesstatic void
61165668Skrisprocess_fsetstat(void)
61265668Skris{
61365668Skris	Attrib *a;
61465668Skris	u_int32_t id;
61565668Skris	int handle, fd, ret;
61676259Sgreen	int status = SSH2_FX_OK;
61765668Skris
61865668Skris	id = get_int();
61965668Skris	handle = get_handle();
62065668Skris	a = get_attrib();
62165668Skris	TRACE("fsetstat id %d handle %d", id, handle);
62265668Skris	fd = handle_to_fd(handle);
62365668Skris	if (fd < 0) {
62476259Sgreen		status = SSH2_FX_FAILURE;
62565668Skris	} else {
62692555Sdes		if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
62792555Sdes			ret = ftruncate(fd, a->size);
62892555Sdes			if (ret == -1)
62992555Sdes				status = errno_to_portable(errno);
63092555Sdes		}
63176259Sgreen		if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
63265668Skris			ret = fchmod(fd, a->perm & 0777);
63365668Skris			if (ret == -1)
63465668Skris				status = errno_to_portable(errno);
63565668Skris		}
63676259Sgreen		if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
63765668Skris			ret = futimes(fd, attrib_to_tv(a));
63865668Skris			if (ret == -1)
63965668Skris				status = errno_to_portable(errno);
64065668Skris		}
64176259Sgreen		if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
64276259Sgreen			ret = fchown(fd, a->uid, a->gid);
64376259Sgreen			if (ret == -1)
64476259Sgreen				status = errno_to_portable(errno);
64576259Sgreen		}
64665668Skris	}
64765668Skris	send_status(id, status);
64865668Skris}
64965668Skris
65092555Sdesstatic void
65165668Skrisprocess_opendir(void)
65265668Skris{
65365668Skris	DIR *dirp = NULL;
65465668Skris	char *path;
65576259Sgreen	int handle, status = SSH2_FX_FAILURE;
65665668Skris	u_int32_t id;
65765668Skris
65865668Skris	id = get_int();
65965668Skris	path = get_string(NULL);
66065668Skris	TRACE("opendir id %d path %s", id, path);
66176259Sgreen	dirp = opendir(path);
66265668Skris	if (dirp == NULL) {
66365668Skris		status = errno_to_portable(errno);
66465668Skris	} else {
66565668Skris		handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp);
66665668Skris		if (handle < 0) {
66765668Skris			closedir(dirp);
66865668Skris		} else {
66965668Skris			send_handle(id, handle);
67076259Sgreen			status = SSH2_FX_OK;
67165668Skris		}
67276259Sgreen
67365668Skris	}
67476259Sgreen	if (status != SSH2_FX_OK)
67565668Skris		send_status(id, status);
67665668Skris	xfree(path);
67765668Skris}
67865668Skris
67976259Sgreen/*
68076259Sgreen * drwxr-xr-x    5 markus   markus       1024 Jan 13 18:39 .ssh
68176259Sgreen */
68292555Sdesstatic char *
68365668Skrisls_file(char *name, struct stat *st)
68465668Skris{
68592555Sdes	int ulen, glen, sz = 0;
68676259Sgreen	struct passwd *pw;
68776259Sgreen	struct group *gr;
68876259Sgreen	struct tm *ltime = localtime(&st->st_mtime);
68976259Sgreen	char *user, *group;
69076259Sgreen	char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1];
69176259Sgreen
69276259Sgreen	strmode(st->st_mode, mode);
69376259Sgreen	if ((pw = getpwuid(st->st_uid)) != NULL) {
69476259Sgreen		user = pw->pw_name;
69576259Sgreen	} else {
69676259Sgreen		snprintf(ubuf, sizeof ubuf, "%d", st->st_uid);
69776259Sgreen		user = ubuf;
69876259Sgreen	}
69976259Sgreen	if ((gr = getgrgid(st->st_gid)) != NULL) {
70076259Sgreen		group = gr->gr_name;
70176259Sgreen	} else {
70276259Sgreen		snprintf(gbuf, sizeof gbuf, "%d", st->st_gid);
70376259Sgreen		group = gbuf;
70476259Sgreen	}
70576259Sgreen	if (ltime != NULL) {
70676259Sgreen		if (time(NULL) - st->st_mtime < (365*24*60*60)/2)
70776259Sgreen			sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime);
70876259Sgreen		else
70976259Sgreen			sz = strftime(tbuf, sizeof tbuf, "%b %e  %Y", ltime);
71076259Sgreen	}
71176259Sgreen	if (sz == 0)
71276259Sgreen		tbuf[0] = '\0';
71392555Sdes	ulen = MAX(strlen(user), 8);
71492555Sdes	glen = MAX(strlen(group), 8);
71592555Sdes	snprintf(buf, sizeof buf, "%s %3d %-*s %-*s %8llu %s %s", mode,
71692555Sdes	    st->st_nlink, ulen, user, glen, group,
71792555Sdes	    (unsigned long long)st->st_size, tbuf, name);
71865668Skris	return xstrdup(buf);
71965668Skris}
72065668Skris
72192555Sdesstatic void
72265668Skrisprocess_readdir(void)
72365668Skris{
72465668Skris	DIR *dirp;
72565668Skris	struct dirent *dp;
72665668Skris	char *path;
72765668Skris	int handle;
72865668Skris	u_int32_t id;
72965668Skris
73065668Skris	id = get_int();
73165668Skris	handle = get_handle();
73265668Skris	TRACE("readdir id %d handle %d", id, handle);
73365668Skris	dirp = handle_to_dir(handle);
73465668Skris	path = handle_to_name(handle);
73565668Skris	if (dirp == NULL || path == NULL) {
73676259Sgreen		send_status(id, SSH2_FX_FAILURE);
73765668Skris	} else {
73865668Skris		struct stat st;
73965668Skris		char pathname[1024];
74065668Skris		Stat *stats;
74165668Skris		int nstats = 10, count = 0, i;
74265668Skris		stats = xmalloc(nstats * sizeof(Stat));
74365668Skris		while ((dp = readdir(dirp)) != NULL) {
74465668Skris			if (count >= nstats) {
74565668Skris				nstats *= 2;
74665668Skris				stats = xrealloc(stats, nstats * sizeof(Stat));
74765668Skris			}
74865668Skris/* XXX OVERFLOW ? */
74992555Sdes			snprintf(pathname, sizeof pathname, "%s%s%s", path,
75092555Sdes			    strcmp(path, "/") ? "/" : "", dp->d_name);
75165668Skris			if (lstat(pathname, &st) < 0)
75265668Skris				continue;
75376259Sgreen			stat_to_attrib(&st, &(stats[count].attrib));
75465668Skris			stats[count].name = xstrdup(dp->d_name);
75565668Skris			stats[count].long_name = ls_file(dp->d_name, &st);
75665668Skris			count++;
75765668Skris			/* send up to 100 entries in one message */
75876259Sgreen			/* XXX check packet size instead */
75965668Skris			if (count == 100)
76065668Skris				break;
76165668Skris		}
76276259Sgreen		if (count > 0) {
76376259Sgreen			send_names(id, count, stats);
76492555Sdes			for (i = 0; i < count; i++) {
76576259Sgreen				xfree(stats[i].name);
76676259Sgreen				xfree(stats[i].long_name);
76776259Sgreen			}
76876259Sgreen		} else {
76976259Sgreen			send_status(id, SSH2_FX_EOF);
77065668Skris		}
77165668Skris		xfree(stats);
77265668Skris	}
77365668Skris}
77465668Skris
77592555Sdesstatic void
77665668Skrisprocess_remove(void)
77765668Skris{
77865668Skris	char *name;
77965668Skris	u_int32_t id;
78076259Sgreen	int status = SSH2_FX_FAILURE;
78165668Skris	int ret;
78265668Skris
78365668Skris	id = get_int();
78465668Skris	name = get_string(NULL);
78565668Skris	TRACE("remove id %d name %s", id, name);
78676259Sgreen	ret = unlink(name);
78776259Sgreen	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
78865668Skris	send_status(id, status);
78965668Skris	xfree(name);
79065668Skris}
79165668Skris
79292555Sdesstatic void
79365668Skrisprocess_mkdir(void)
79465668Skris{
79565668Skris	Attrib *a;
79665668Skris	u_int32_t id;
79765668Skris	char *name;
79876259Sgreen	int ret, mode, status = SSH2_FX_FAILURE;
79965668Skris
80065668Skris	id = get_int();
80165668Skris	name = get_string(NULL);
80265668Skris	a = get_attrib();
80376259Sgreen	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
80476259Sgreen	    a->perm & 0777 : 0777;
80565668Skris	TRACE("mkdir id %d name %s mode 0%o", id, name, mode);
80665668Skris	ret = mkdir(name, mode);
80776259Sgreen	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
80865668Skris	send_status(id, status);
80965668Skris	xfree(name);
81065668Skris}
81165668Skris
81292555Sdesstatic void
81365668Skrisprocess_rmdir(void)
81465668Skris{
81565668Skris	u_int32_t id;
81665668Skris	char *name;
81765668Skris	int ret, status;
81865668Skris
81965668Skris	id = get_int();
82065668Skris	name = get_string(NULL);
82165668Skris	TRACE("rmdir id %d name %s", id, name);
82265668Skris	ret = rmdir(name);
82376259Sgreen	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
82465668Skris	send_status(id, status);
82565668Skris	xfree(name);
82665668Skris}
82765668Skris
82892555Sdesstatic void
82965668Skrisprocess_realpath(void)
83065668Skris{
83165668Skris	char resolvedname[MAXPATHLEN];
83265668Skris	u_int32_t id;
83365668Skris	char *path;
83465668Skris
83565668Skris	id = get_int();
83665668Skris	path = get_string(NULL);
83776259Sgreen	if (path[0] == '\0') {
83876259Sgreen		xfree(path);
83976259Sgreen		path = xstrdup(".");
84076259Sgreen	}
84165668Skris	TRACE("realpath id %d path %s", id, path);
84265668Skris	if (realpath(path, resolvedname) == NULL) {
84365668Skris		send_status(id, errno_to_portable(errno));
84465668Skris	} else {
84565668Skris		Stat s;
84665668Skris		attrib_clear(&s.attrib);
84765668Skris		s.name = s.long_name = resolvedname;
84865668Skris		send_names(id, 1, &s);
84965668Skris	}
85065668Skris	xfree(path);
85165668Skris}
85265668Skris
85392555Sdesstatic void
85465668Skrisprocess_rename(void)
85565668Skris{
85665668Skris	u_int32_t id;
85776259Sgreen	struct stat st;
85865668Skris	char *oldpath, *newpath;
85976259Sgreen	int ret, status = SSH2_FX_FAILURE;
86065668Skris
86165668Skris	id = get_int();
86265668Skris	oldpath = get_string(NULL);
86365668Skris	newpath = get_string(NULL);
86465668Skris	TRACE("rename id %d old %s new %s", id, oldpath, newpath);
86576259Sgreen	/* fail if 'newpath' exists */
86676259Sgreen	if (stat(newpath, &st) == -1) {
86776259Sgreen		ret = rename(oldpath, newpath);
86876259Sgreen		status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
86976259Sgreen	}
87065668Skris	send_status(id, status);
87165668Skris	xfree(oldpath);
87265668Skris	xfree(newpath);
87365668Skris}
87465668Skris
87592555Sdesstatic void
87676259Sgreenprocess_readlink(void)
87776259Sgreen{
87876259Sgreen	u_int32_t id;
87992555Sdes	int len;
88076259Sgreen	char link[MAXPATHLEN];
88176259Sgreen	char *path;
88265668Skris
88376259Sgreen	id = get_int();
88476259Sgreen	path = get_string(NULL);
88576259Sgreen	TRACE("readlink id %d path %s", id, path);
88692555Sdes	if ((len = readlink(path, link, sizeof(link) - 1)) == -1)
88776259Sgreen		send_status(id, errno_to_portable(errno));
88876259Sgreen	else {
88976259Sgreen		Stat s;
89092555Sdes
89192555Sdes		link[len] = '\0';
89276259Sgreen		attrib_clear(&s.attrib);
89376259Sgreen		s.name = s.long_name = link;
89476259Sgreen		send_names(id, 1, &s);
89576259Sgreen	}
89676259Sgreen	xfree(path);
89776259Sgreen}
89876259Sgreen
89992555Sdesstatic void
90076259Sgreenprocess_symlink(void)
90176259Sgreen{
90276259Sgreen	u_int32_t id;
90376259Sgreen	struct stat st;
90476259Sgreen	char *oldpath, *newpath;
90576259Sgreen	int ret, status = SSH2_FX_FAILURE;
90676259Sgreen
90776259Sgreen	id = get_int();
90876259Sgreen	oldpath = get_string(NULL);
90976259Sgreen	newpath = get_string(NULL);
91076259Sgreen	TRACE("symlink id %d old %s new %s", id, oldpath, newpath);
91176259Sgreen	/* fail if 'newpath' exists */
91276259Sgreen	if (stat(newpath, &st) == -1) {
91376259Sgreen		ret = symlink(oldpath, newpath);
91476259Sgreen		status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
91576259Sgreen	}
91676259Sgreen	send_status(id, status);
91776259Sgreen	xfree(oldpath);
91876259Sgreen	xfree(newpath);
91976259Sgreen}
92076259Sgreen
92192555Sdesstatic void
92276259Sgreenprocess_extended(void)
92376259Sgreen{
92476259Sgreen	u_int32_t id;
92576259Sgreen	char *request;
92676259Sgreen
92776259Sgreen	id = get_int();
92876259Sgreen	request = get_string(NULL);
92976259Sgreen	send_status(id, SSH2_FX_OP_UNSUPPORTED);		/* MUST */
93076259Sgreen	xfree(request);
93176259Sgreen}
93276259Sgreen
93365668Skris/* stolen from ssh-agent */
93465668Skris
93592555Sdesstatic void
93665668Skrisprocess(void)
93765668Skris{
93876259Sgreen	u_int msg_len;
93976259Sgreen	u_int type;
94076259Sgreen	u_char *cp;
94165668Skris
94265668Skris	if (buffer_len(&iqueue) < 5)
94365668Skris		return;		/* Incomplete message. */
94492555Sdes	cp = buffer_ptr(&iqueue);
94565668Skris	msg_len = GET_32BIT(cp);
94665668Skris	if (msg_len > 256 * 1024) {
94765668Skris		error("bad message ");
94865668Skris		exit(11);
94965668Skris	}
95065668Skris	if (buffer_len(&iqueue) < msg_len + 4)
95165668Skris		return;
95265668Skris	buffer_consume(&iqueue, 4);
95365668Skris	type = buffer_get_char(&iqueue);
95465668Skris	switch (type) {
95576259Sgreen	case SSH2_FXP_INIT:
95665668Skris		process_init();
95765668Skris		break;
95876259Sgreen	case SSH2_FXP_OPEN:
95965668Skris		process_open();
96065668Skris		break;
96176259Sgreen	case SSH2_FXP_CLOSE:
96265668Skris		process_close();
96365668Skris		break;
96476259Sgreen	case SSH2_FXP_READ:
96565668Skris		process_read();
96665668Skris		break;
96776259Sgreen	case SSH2_FXP_WRITE:
96865668Skris		process_write();
96965668Skris		break;
97076259Sgreen	case SSH2_FXP_LSTAT:
97165668Skris		process_lstat();
97265668Skris		break;
97376259Sgreen	case SSH2_FXP_FSTAT:
97465668Skris		process_fstat();
97565668Skris		break;
97676259Sgreen	case SSH2_FXP_SETSTAT:
97765668Skris		process_setstat();
97865668Skris		break;
97976259Sgreen	case SSH2_FXP_FSETSTAT:
98065668Skris		process_fsetstat();
98165668Skris		break;
98276259Sgreen	case SSH2_FXP_OPENDIR:
98365668Skris		process_opendir();
98465668Skris		break;
98576259Sgreen	case SSH2_FXP_READDIR:
98665668Skris		process_readdir();
98765668Skris		break;
98876259Sgreen	case SSH2_FXP_REMOVE:
98965668Skris		process_remove();
99065668Skris		break;
99176259Sgreen	case SSH2_FXP_MKDIR:
99265668Skris		process_mkdir();
99365668Skris		break;
99476259Sgreen	case SSH2_FXP_RMDIR:
99565668Skris		process_rmdir();
99665668Skris		break;
99776259Sgreen	case SSH2_FXP_REALPATH:
99865668Skris		process_realpath();
99965668Skris		break;
100076259Sgreen	case SSH2_FXP_STAT:
100165668Skris		process_stat();
100265668Skris		break;
100376259Sgreen	case SSH2_FXP_RENAME:
100465668Skris		process_rename();
100565668Skris		break;
100676259Sgreen	case SSH2_FXP_READLINK:
100776259Sgreen		process_readlink();
100876259Sgreen		break;
100976259Sgreen	case SSH2_FXP_SYMLINK:
101076259Sgreen		process_symlink();
101176259Sgreen		break;
101276259Sgreen	case SSH2_FXP_EXTENDED:
101376259Sgreen		process_extended();
101476259Sgreen		break;
101565668Skris	default:
101665668Skris		error("Unknown message %d", type);
101765668Skris		break;
101865668Skris	}
101965668Skris}
102065668Skris
102165668Skrisint
102265668Skrismain(int ac, char **av)
102365668Skris{
102476259Sgreen	fd_set *rset, *wset;
102565668Skris	int in, out, max;
102676259Sgreen	ssize_t len, olen, set_size;
102765668Skris
102876259Sgreen	/* XXX should use getopt */
102976259Sgreen
103065668Skris	handle_init();
103165668Skris
103276259Sgreen#ifdef DEBUG_SFTP_SERVER
103376259Sgreen	log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
103476259Sgreen#endif
103576259Sgreen
103665668Skris	in = dup(STDIN_FILENO);
103765668Skris	out = dup(STDOUT_FILENO);
103865668Skris
103965668Skris	max = 0;
104065668Skris	if (in > max)
104165668Skris		max = in;
104265668Skris	if (out > max)
104365668Skris		max = out;
104465668Skris
104565668Skris	buffer_init(&iqueue);
104665668Skris	buffer_init(&oqueue);
104765668Skris
104876259Sgreen	set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
104976259Sgreen	rset = (fd_set *)xmalloc(set_size);
105076259Sgreen	wset = (fd_set *)xmalloc(set_size);
105176259Sgreen
105265668Skris	for (;;) {
105376259Sgreen		memset(rset, 0, set_size);
105476259Sgreen		memset(wset, 0, set_size);
105565668Skris
105676259Sgreen		FD_SET(in, rset);
105765668Skris		olen = buffer_len(&oqueue);
105865668Skris		if (olen > 0)
105976259Sgreen			FD_SET(out, wset);
106065668Skris
106176259Sgreen		if (select(max+1, rset, wset, NULL, NULL) < 0) {
106265668Skris			if (errno == EINTR)
106365668Skris				continue;
106465668Skris			exit(2);
106565668Skris		}
106665668Skris
106765668Skris		/* copy stdin to iqueue */
106876259Sgreen		if (FD_ISSET(in, rset)) {
106965668Skris			char buf[4*4096];
107065668Skris			len = read(in, buf, sizeof buf);
107165668Skris			if (len == 0) {
107265668Skris				debug("read eof");
107365668Skris				exit(0);
107465668Skris			} else if (len < 0) {
107565668Skris				error("read error");
107665668Skris				exit(1);
107765668Skris			} else {
107865668Skris				buffer_append(&iqueue, buf, len);
107965668Skris			}
108065668Skris		}
108165668Skris		/* send oqueue to stdout */
108276259Sgreen		if (FD_ISSET(out, wset)) {
108365668Skris			len = write(out, buffer_ptr(&oqueue), olen);
108465668Skris			if (len < 0) {
108565668Skris				error("write error");
108665668Skris				exit(1);
108765668Skris			} else {
108865668Skris				buffer_consume(&oqueue, len);
108965668Skris			}
109065668Skris		}
109165668Skris		/* process requests from client */
109265668Skris		process();
109365668Skris	}
109465668Skris}
1095