sftp-server.c revision 99060
1/*
2 * Copyright (c) 2000, 2001, 2002 Markus Friedl.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24#include "includes.h"
25RCSID("$OpenBSD: sftp-server.c,v 1.37 2002/06/24 17:57:20 deraadt Exp $");
26
27#include "buffer.h"
28#include "bufaux.h"
29#include "getput.h"
30#include "log.h"
31#include "xmalloc.h"
32
33#include "sftp.h"
34#include "sftp-common.h"
35
36/* helper */
37#define get_int64()			buffer_get_int64(&iqueue);
38#define get_int()			buffer_get_int(&iqueue);
39#define get_string(lenp)		buffer_get_string(&iqueue, lenp);
40#define TRACE				debug
41
42#ifdef HAVE___PROGNAME
43extern char *__progname;
44#else
45char *__progname;
46#endif
47
48/* input and output queue */
49Buffer iqueue;
50Buffer oqueue;
51
52/* Version of client */
53int version;
54
55/* portable attibutes, etc. */
56
57typedef struct Stat Stat;
58
59struct Stat {
60	char *name;
61	char *long_name;
62	Attrib attrib;
63};
64
65static int
66errno_to_portable(int unixerrno)
67{
68	int ret = 0;
69
70	switch (unixerrno) {
71	case 0:
72		ret = SSH2_FX_OK;
73		break;
74	case ENOENT:
75	case ENOTDIR:
76	case EBADF:
77	case ELOOP:
78		ret = SSH2_FX_NO_SUCH_FILE;
79		break;
80	case EPERM:
81	case EACCES:
82	case EFAULT:
83		ret = SSH2_FX_PERMISSION_DENIED;
84		break;
85	case ENAMETOOLONG:
86	case EINVAL:
87		ret = SSH2_FX_BAD_MESSAGE;
88		break;
89	default:
90		ret = SSH2_FX_FAILURE;
91		break;
92	}
93	return ret;
94}
95
96static int
97flags_from_portable(int pflags)
98{
99	int flags = 0;
100
101	if ((pflags & SSH2_FXF_READ) &&
102	    (pflags & SSH2_FXF_WRITE)) {
103		flags = O_RDWR;
104	} else if (pflags & SSH2_FXF_READ) {
105		flags = O_RDONLY;
106	} else if (pflags & SSH2_FXF_WRITE) {
107		flags = O_WRONLY;
108	}
109	if (pflags & SSH2_FXF_CREAT)
110		flags |= O_CREAT;
111	if (pflags & SSH2_FXF_TRUNC)
112		flags |= O_TRUNC;
113	if (pflags & SSH2_FXF_EXCL)
114		flags |= O_EXCL;
115	return flags;
116}
117
118static Attrib *
119get_attrib(void)
120{
121	return decode_attrib(&iqueue);
122}
123
124/* handle handles */
125
126typedef struct Handle Handle;
127struct Handle {
128	int use;
129	DIR *dirp;
130	int fd;
131	char *name;
132};
133
134enum {
135	HANDLE_UNUSED,
136	HANDLE_DIR,
137	HANDLE_FILE
138};
139
140Handle	handles[100];
141
142static void
143handle_init(void)
144{
145	int i;
146
147	for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
148		handles[i].use = HANDLE_UNUSED;
149}
150
151static int
152handle_new(int use, char *name, int fd, DIR *dirp)
153{
154	int i;
155
156	for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
157		if (handles[i].use == HANDLE_UNUSED) {
158			handles[i].use = use;
159			handles[i].dirp = dirp;
160			handles[i].fd = fd;
161			handles[i].name = name;
162			return i;
163		}
164	}
165	return -1;
166}
167
168static int
169handle_is_ok(int i, int type)
170{
171	return i >= 0 && i < sizeof(handles)/sizeof(Handle) &&
172	    handles[i].use == type;
173}
174
175static int
176handle_to_string(int handle, char **stringp, int *hlenp)
177{
178	if (stringp == NULL || hlenp == NULL)
179		return -1;
180	*stringp = xmalloc(sizeof(int32_t));
181	PUT_32BIT(*stringp, handle);
182	*hlenp = sizeof(int32_t);
183	return 0;
184}
185
186static int
187handle_from_string(char *handle, u_int hlen)
188{
189	int val;
190
191	if (hlen != sizeof(int32_t))
192		return -1;
193	val = GET_32BIT(handle);
194	if (handle_is_ok(val, HANDLE_FILE) ||
195	    handle_is_ok(val, HANDLE_DIR))
196		return val;
197	return -1;
198}
199
200static char *
201handle_to_name(int handle)
202{
203	if (handle_is_ok(handle, HANDLE_DIR)||
204	    handle_is_ok(handle, HANDLE_FILE))
205		return handles[handle].name;
206	return NULL;
207}
208
209static DIR *
210handle_to_dir(int handle)
211{
212	if (handle_is_ok(handle, HANDLE_DIR))
213		return handles[handle].dirp;
214	return NULL;
215}
216
217static int
218handle_to_fd(int handle)
219{
220	if (handle_is_ok(handle, HANDLE_FILE))
221		return handles[handle].fd;
222	return -1;
223}
224
225static int
226handle_close(int handle)
227{
228	int ret = -1;
229
230	if (handle_is_ok(handle, HANDLE_FILE)) {
231		ret = close(handles[handle].fd);
232		handles[handle].use = HANDLE_UNUSED;
233	} else if (handle_is_ok(handle, HANDLE_DIR)) {
234		ret = closedir(handles[handle].dirp);
235		handles[handle].use = HANDLE_UNUSED;
236	} else {
237		errno = ENOENT;
238	}
239	return ret;
240}
241
242static int
243get_handle(void)
244{
245	char *handle;
246	int val = -1;
247	u_int hlen;
248
249	handle = get_string(&hlen);
250	if (hlen < 256)
251		val = handle_from_string(handle, hlen);
252	xfree(handle);
253	return val;
254}
255
256/* send replies */
257
258static void
259send_msg(Buffer *m)
260{
261	int mlen = buffer_len(m);
262
263	buffer_put_int(&oqueue, mlen);
264	buffer_append(&oqueue, buffer_ptr(m), mlen);
265	buffer_consume(m, mlen);
266}
267
268static void
269send_status(u_int32_t id, u_int32_t error)
270{
271	Buffer msg;
272	const char *status_messages[] = {
273		"Success",			/* SSH_FX_OK */
274		"End of file",			/* SSH_FX_EOF */
275		"No such file",			/* SSH_FX_NO_SUCH_FILE */
276		"Permission denied",		/* SSH_FX_PERMISSION_DENIED */
277		"Failure",			/* SSH_FX_FAILURE */
278		"Bad message",			/* SSH_FX_BAD_MESSAGE */
279		"No connection",		/* SSH_FX_NO_CONNECTION */
280		"Connection lost",		/* SSH_FX_CONNECTION_LOST */
281		"Operation unsupported",	/* SSH_FX_OP_UNSUPPORTED */
282		"Unknown error"			/* Others */
283	};
284
285	TRACE("sent status id %u error %u", id, error);
286	buffer_init(&msg);
287	buffer_put_char(&msg, SSH2_FXP_STATUS);
288	buffer_put_int(&msg, id);
289	buffer_put_int(&msg, error);
290	if (version >= 3) {
291		buffer_put_cstring(&msg,
292		    status_messages[MIN(error,SSH2_FX_MAX)]);
293		buffer_put_cstring(&msg, "");
294	}
295	send_msg(&msg);
296	buffer_free(&msg);
297}
298static void
299send_data_or_handle(char type, u_int32_t id, char *data, int dlen)
300{
301	Buffer msg;
302
303	buffer_init(&msg);
304	buffer_put_char(&msg, type);
305	buffer_put_int(&msg, id);
306	buffer_put_string(&msg, data, dlen);
307	send_msg(&msg);
308	buffer_free(&msg);
309}
310
311static void
312send_data(u_int32_t id, char *data, int dlen)
313{
314	TRACE("sent data id %u len %d", id, dlen);
315	send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
316}
317
318static void
319send_handle(u_int32_t id, int handle)
320{
321	char *string;
322	int hlen;
323
324	handle_to_string(handle, &string, &hlen);
325	TRACE("sent handle id %u handle %d", id, handle);
326	send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
327	xfree(string);
328}
329
330static void
331send_names(u_int32_t id, int count, Stat *stats)
332{
333	Buffer msg;
334	int i;
335
336	buffer_init(&msg);
337	buffer_put_char(&msg, SSH2_FXP_NAME);
338	buffer_put_int(&msg, id);
339	buffer_put_int(&msg, count);
340	TRACE("sent names id %u count %d", id, count);
341	for (i = 0; i < count; i++) {
342		buffer_put_cstring(&msg, stats[i].name);
343		buffer_put_cstring(&msg, stats[i].long_name);
344		encode_attrib(&msg, &stats[i].attrib);
345	}
346	send_msg(&msg);
347	buffer_free(&msg);
348}
349
350static void
351send_attrib(u_int32_t id, Attrib *a)
352{
353	Buffer msg;
354
355	TRACE("sent attrib id %u have 0x%x", id, a->flags);
356	buffer_init(&msg);
357	buffer_put_char(&msg, SSH2_FXP_ATTRS);
358	buffer_put_int(&msg, id);
359	encode_attrib(&msg, a);
360	send_msg(&msg);
361	buffer_free(&msg);
362}
363
364/* parse incoming */
365
366static void
367process_init(void)
368{
369	Buffer msg;
370
371	version = get_int();
372	TRACE("client version %d", version);
373	buffer_init(&msg);
374	buffer_put_char(&msg, SSH2_FXP_VERSION);
375	buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
376	send_msg(&msg);
377	buffer_free(&msg);
378}
379
380static void
381process_open(void)
382{
383	u_int32_t id, pflags;
384	Attrib *a;
385	char *name;
386	int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
387
388	id = get_int();
389	name = get_string(NULL);
390	pflags = get_int();		/* portable flags */
391	a = get_attrib();
392	flags = flags_from_portable(pflags);
393	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
394	TRACE("open id %u name %s flags %d mode 0%o", id, name, pflags, mode);
395	fd = open(name, flags, mode);
396	if (fd < 0) {
397		status = errno_to_portable(errno);
398	} else {
399		handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL);
400		if (handle < 0) {
401			close(fd);
402		} else {
403			send_handle(id, handle);
404			status = SSH2_FX_OK;
405		}
406	}
407	if (status != SSH2_FX_OK)
408		send_status(id, status);
409	xfree(name);
410}
411
412static void
413process_close(void)
414{
415	u_int32_t id;
416	int handle, ret, status = SSH2_FX_FAILURE;
417
418	id = get_int();
419	handle = get_handle();
420	TRACE("close id %u handle %d", id, handle);
421	ret = handle_close(handle);
422	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
423	send_status(id, status);
424}
425
426static void
427process_read(void)
428{
429	char buf[64*1024];
430	u_int32_t id, len;
431	int handle, fd, ret, status = SSH2_FX_FAILURE;
432	u_int64_t off;
433
434	id = get_int();
435	handle = get_handle();
436	off = get_int64();
437	len = get_int();
438
439	TRACE("read id %u handle %d off %llu len %d", id, handle,
440	    (u_int64_t)off, len);
441	if (len > sizeof buf) {
442		len = sizeof buf;
443		log("read change len %d", len);
444	}
445	fd = handle_to_fd(handle);
446	if (fd >= 0) {
447		if (lseek(fd, off, SEEK_SET) < 0) {
448			error("process_read: seek failed");
449			status = errno_to_portable(errno);
450		} else {
451			ret = read(fd, buf, len);
452			if (ret < 0) {
453				status = errno_to_portable(errno);
454			} else if (ret == 0) {
455				status = SSH2_FX_EOF;
456			} else {
457				send_data(id, buf, ret);
458				status = SSH2_FX_OK;
459			}
460		}
461	}
462	if (status != SSH2_FX_OK)
463		send_status(id, status);
464}
465
466static void
467process_write(void)
468{
469	u_int32_t id;
470	u_int64_t off;
471	u_int len;
472	int handle, fd, ret, status = SSH2_FX_FAILURE;
473	char *data;
474
475	id = get_int();
476	handle = get_handle();
477	off = get_int64();
478	data = get_string(&len);
479
480	TRACE("write id %u handle %d off %llu len %d", id, handle,
481	    (u_int64_t)off, len);
482	fd = handle_to_fd(handle);
483	if (fd >= 0) {
484		if (lseek(fd, off, SEEK_SET) < 0) {
485			status = errno_to_portable(errno);
486			error("process_write: seek failed");
487		} else {
488/* XXX ATOMICIO ? */
489			ret = write(fd, data, len);
490			if (ret == -1) {
491				error("process_write: write failed");
492				status = errno_to_portable(errno);
493			} else if (ret == len) {
494				status = SSH2_FX_OK;
495			} else {
496				log("nothing at all written");
497			}
498		}
499	}
500	send_status(id, status);
501	xfree(data);
502}
503
504static void
505process_do_stat(int do_lstat)
506{
507	Attrib a;
508	struct stat st;
509	u_int32_t id;
510	char *name;
511	int ret, status = SSH2_FX_FAILURE;
512
513	id = get_int();
514	name = get_string(NULL);
515	TRACE("%sstat id %u name %s", do_lstat ? "l" : "", id, name);
516	ret = do_lstat ? lstat(name, &st) : stat(name, &st);
517	if (ret < 0) {
518		status = errno_to_portable(errno);
519	} else {
520		stat_to_attrib(&st, &a);
521		send_attrib(id, &a);
522		status = SSH2_FX_OK;
523	}
524	if (status != SSH2_FX_OK)
525		send_status(id, status);
526	xfree(name);
527}
528
529static void
530process_stat(void)
531{
532	process_do_stat(0);
533}
534
535static void
536process_lstat(void)
537{
538	process_do_stat(1);
539}
540
541static void
542process_fstat(void)
543{
544	Attrib a;
545	struct stat st;
546	u_int32_t id;
547	int fd, ret, handle, status = SSH2_FX_FAILURE;
548
549	id = get_int();
550	handle = get_handle();
551	TRACE("fstat id %u handle %d", id, handle);
552	fd = handle_to_fd(handle);
553	if (fd  >= 0) {
554		ret = fstat(fd, &st);
555		if (ret < 0) {
556			status = errno_to_portable(errno);
557		} else {
558			stat_to_attrib(&st, &a);
559			send_attrib(id, &a);
560			status = SSH2_FX_OK;
561		}
562	}
563	if (status != SSH2_FX_OK)
564		send_status(id, status);
565}
566
567static struct timeval *
568attrib_to_tv(Attrib *a)
569{
570	static struct timeval tv[2];
571
572	tv[0].tv_sec = a->atime;
573	tv[0].tv_usec = 0;
574	tv[1].tv_sec = a->mtime;
575	tv[1].tv_usec = 0;
576	return tv;
577}
578
579static void
580process_setstat(void)
581{
582	Attrib *a;
583	u_int32_t id;
584	char *name;
585	int status = SSH2_FX_OK, ret;
586
587	id = get_int();
588	name = get_string(NULL);
589	a = get_attrib();
590	TRACE("setstat id %u name %s", id, name);
591	if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
592		ret = truncate(name, a->size);
593		if (ret == -1)
594			status = errno_to_portable(errno);
595	}
596	if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
597		ret = chmod(name, a->perm & 0777);
598		if (ret == -1)
599			status = errno_to_portable(errno);
600	}
601	if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
602		ret = utimes(name, attrib_to_tv(a));
603		if (ret == -1)
604			status = errno_to_portable(errno);
605	}
606	if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
607		ret = chown(name, a->uid, a->gid);
608		if (ret == -1)
609			status = errno_to_portable(errno);
610	}
611	send_status(id, status);
612	xfree(name);
613}
614
615static void
616process_fsetstat(void)
617{
618	Attrib *a;
619	u_int32_t id;
620	int handle, fd, ret;
621	int status = SSH2_FX_OK;
622	char *name;
623
624	id = get_int();
625	handle = get_handle();
626	a = get_attrib();
627	TRACE("fsetstat id %u handle %d", id, handle);
628	fd = handle_to_fd(handle);
629	name = handle_to_name(handle);
630	if (fd < 0 || name == NULL) {
631		status = SSH2_FX_FAILURE;
632	} else {
633		if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
634			ret = ftruncate(fd, a->size);
635			if (ret == -1)
636				status = errno_to_portable(errno);
637		}
638		if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
639#ifdef HAVE_FCHMOD
640			ret = fchmod(fd, a->perm & 0777);
641#else
642			ret = chmod(name, a->perm & 0777);
643#endif
644			if (ret == -1)
645				status = errno_to_portable(errno);
646		}
647		if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
648#ifdef HAVE_FUTIMES
649			ret = futimes(fd, attrib_to_tv(a));
650#else
651			ret = utimes(name, attrib_to_tv(a));
652#endif
653			if (ret == -1)
654				status = errno_to_portable(errno);
655		}
656		if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
657#ifdef HAVE_FCHOWN
658			ret = fchown(fd, a->uid, a->gid);
659#else
660			ret = chown(name, a->uid, a->gid);
661#endif
662			if (ret == -1)
663				status = errno_to_portable(errno);
664		}
665	}
666	send_status(id, status);
667}
668
669static void
670process_opendir(void)
671{
672	DIR *dirp = NULL;
673	char *path;
674	int handle, status = SSH2_FX_FAILURE;
675	u_int32_t id;
676
677	id = get_int();
678	path = get_string(NULL);
679	TRACE("opendir id %u path %s", id, path);
680	dirp = opendir(path);
681	if (dirp == NULL) {
682		status = errno_to_portable(errno);
683	} else {
684		handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp);
685		if (handle < 0) {
686			closedir(dirp);
687		} else {
688			send_handle(id, handle);
689			status = SSH2_FX_OK;
690		}
691
692	}
693	if (status != SSH2_FX_OK)
694		send_status(id, status);
695	xfree(path);
696}
697
698/*
699 * drwxr-xr-x    5 markus   markus       1024 Jan 13 18:39 .ssh
700 */
701static char *
702ls_file(char *name, struct stat *st)
703{
704	int ulen, glen, sz = 0;
705	struct passwd *pw;
706	struct group *gr;
707	struct tm *ltime = localtime(&st->st_mtime);
708	char *user, *group;
709	char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1];
710
711	strmode(st->st_mode, mode);
712	if ((pw = getpwuid(st->st_uid)) != NULL) {
713		user = pw->pw_name;
714	} else {
715		snprintf(ubuf, sizeof ubuf, "%u", (u_int)st->st_uid);
716		user = ubuf;
717	}
718	if ((gr = getgrgid(st->st_gid)) != NULL) {
719		group = gr->gr_name;
720	} else {
721		snprintf(gbuf, sizeof gbuf, "%u", (u_int)st->st_gid);
722		group = gbuf;
723	}
724	if (ltime != NULL) {
725		if (time(NULL) - st->st_mtime < (365*24*60*60)/2)
726			sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime);
727		else
728			sz = strftime(tbuf, sizeof tbuf, "%b %e  %Y", ltime);
729	}
730	if (sz == 0)
731		tbuf[0] = '\0';
732	ulen = MAX(strlen(user), 8);
733	glen = MAX(strlen(group), 8);
734	snprintf(buf, sizeof buf, "%s %3d %-*s %-*s %8llu %s %s", mode,
735	    st->st_nlink, ulen, user, glen, group,
736	    (u_int64_t)st->st_size, tbuf, name);
737	return xstrdup(buf);
738}
739
740static void
741process_readdir(void)
742{
743	DIR *dirp;
744	struct dirent *dp;
745	char *path;
746	int handle;
747	u_int32_t id;
748
749	id = get_int();
750	handle = get_handle();
751	TRACE("readdir id %u handle %d", id, handle);
752	dirp = handle_to_dir(handle);
753	path = handle_to_name(handle);
754	if (dirp == NULL || path == NULL) {
755		send_status(id, SSH2_FX_FAILURE);
756	} else {
757		struct stat st;
758		char pathname[1024];
759		Stat *stats;
760		int nstats = 10, count = 0, i;
761
762		stats = xmalloc(nstats * sizeof(Stat));
763		while ((dp = readdir(dirp)) != NULL) {
764			if (count >= nstats) {
765				nstats *= 2;
766				stats = xrealloc(stats, nstats * sizeof(Stat));
767			}
768/* XXX OVERFLOW ? */
769			snprintf(pathname, sizeof pathname, "%s%s%s", path,
770			    strcmp(path, "/") ? "/" : "", dp->d_name);
771			if (lstat(pathname, &st) < 0)
772				continue;
773			stat_to_attrib(&st, &(stats[count].attrib));
774			stats[count].name = xstrdup(dp->d_name);
775			stats[count].long_name = ls_file(dp->d_name, &st);
776			count++;
777			/* send up to 100 entries in one message */
778			/* XXX check packet size instead */
779			if (count == 100)
780				break;
781		}
782		if (count > 0) {
783			send_names(id, count, stats);
784			for (i = 0; i < count; i++) {
785				xfree(stats[i].name);
786				xfree(stats[i].long_name);
787			}
788		} else {
789			send_status(id, SSH2_FX_EOF);
790		}
791		xfree(stats);
792	}
793}
794
795static void
796process_remove(void)
797{
798	char *name;
799	u_int32_t id;
800	int status = SSH2_FX_FAILURE;
801	int ret;
802
803	id = get_int();
804	name = get_string(NULL);
805	TRACE("remove id %u name %s", id, name);
806	ret = unlink(name);
807	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
808	send_status(id, status);
809	xfree(name);
810}
811
812static void
813process_mkdir(void)
814{
815	Attrib *a;
816	u_int32_t id;
817	char *name;
818	int ret, mode, status = SSH2_FX_FAILURE;
819
820	id = get_int();
821	name = get_string(NULL);
822	a = get_attrib();
823	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
824	    a->perm & 0777 : 0777;
825	TRACE("mkdir id %u name %s mode 0%o", id, name, mode);
826	ret = mkdir(name, mode);
827	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
828	send_status(id, status);
829	xfree(name);
830}
831
832static void
833process_rmdir(void)
834{
835	u_int32_t id;
836	char *name;
837	int ret, status;
838
839	id = get_int();
840	name = get_string(NULL);
841	TRACE("rmdir id %u name %s", id, name);
842	ret = rmdir(name);
843	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
844	send_status(id, status);
845	xfree(name);
846}
847
848static void
849process_realpath(void)
850{
851	char resolvedname[MAXPATHLEN];
852	u_int32_t id;
853	char *path;
854
855	id = get_int();
856	path = get_string(NULL);
857	if (path[0] == '\0') {
858		xfree(path);
859		path = xstrdup(".");
860	}
861	TRACE("realpath id %u path %s", id, path);
862	if (realpath(path, resolvedname) == NULL) {
863		send_status(id, errno_to_portable(errno));
864	} else {
865		Stat s;
866		attrib_clear(&s.attrib);
867		s.name = s.long_name = resolvedname;
868		send_names(id, 1, &s);
869	}
870	xfree(path);
871}
872
873static void
874process_rename(void)
875{
876	u_int32_t id;
877	struct stat st;
878	char *oldpath, *newpath;
879	int ret, status = SSH2_FX_FAILURE;
880
881	id = get_int();
882	oldpath = get_string(NULL);
883	newpath = get_string(NULL);
884	TRACE("rename id %u old %s new %s", id, oldpath, newpath);
885	/* fail if 'newpath' exists */
886	if (stat(newpath, &st) == -1) {
887		ret = rename(oldpath, newpath);
888		status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
889	}
890	send_status(id, status);
891	xfree(oldpath);
892	xfree(newpath);
893}
894
895static void
896process_readlink(void)
897{
898	u_int32_t id;
899	int len;
900	char link[MAXPATHLEN];
901	char *path;
902
903	id = get_int();
904	path = get_string(NULL);
905	TRACE("readlink id %u path %s", id, path);
906	if ((len = readlink(path, link, sizeof(link) - 1)) == -1)
907		send_status(id, errno_to_portable(errno));
908	else {
909		Stat s;
910
911		link[len] = '\0';
912		attrib_clear(&s.attrib);
913		s.name = s.long_name = link;
914		send_names(id, 1, &s);
915	}
916	xfree(path);
917}
918
919static void
920process_symlink(void)
921{
922	u_int32_t id;
923	struct stat st;
924	char *oldpath, *newpath;
925	int ret, status = SSH2_FX_FAILURE;
926
927	id = get_int();
928	oldpath = get_string(NULL);
929	newpath = get_string(NULL);
930	TRACE("symlink id %u old %s new %s", id, oldpath, newpath);
931	/* fail if 'newpath' exists */
932	if (stat(newpath, &st) == -1) {
933		ret = symlink(oldpath, newpath);
934		status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
935	}
936	send_status(id, status);
937	xfree(oldpath);
938	xfree(newpath);
939}
940
941static void
942process_extended(void)
943{
944	u_int32_t id;
945	char *request;
946
947	id = get_int();
948	request = get_string(NULL);
949	send_status(id, SSH2_FX_OP_UNSUPPORTED);		/* MUST */
950	xfree(request);
951}
952
953/* stolen from ssh-agent */
954
955static void
956process(void)
957{
958	u_int msg_len;
959	u_int buf_len;
960	u_int consumed;
961	u_int type;
962	u_char *cp;
963
964	buf_len = buffer_len(&iqueue);
965	if (buf_len < 5)
966		return;		/* Incomplete message. */
967	cp = buffer_ptr(&iqueue);
968	msg_len = GET_32BIT(cp);
969	if (msg_len > 256 * 1024) {
970		error("bad message ");
971		exit(11);
972	}
973	if (buf_len < msg_len + 4)
974		return;
975	buffer_consume(&iqueue, 4);
976	buf_len -= 4;
977	type = buffer_get_char(&iqueue);
978	switch (type) {
979	case SSH2_FXP_INIT:
980		process_init();
981		break;
982	case SSH2_FXP_OPEN:
983		process_open();
984		break;
985	case SSH2_FXP_CLOSE:
986		process_close();
987		break;
988	case SSH2_FXP_READ:
989		process_read();
990		break;
991	case SSH2_FXP_WRITE:
992		process_write();
993		break;
994	case SSH2_FXP_LSTAT:
995		process_lstat();
996		break;
997	case SSH2_FXP_FSTAT:
998		process_fstat();
999		break;
1000	case SSH2_FXP_SETSTAT:
1001		process_setstat();
1002		break;
1003	case SSH2_FXP_FSETSTAT:
1004		process_fsetstat();
1005		break;
1006	case SSH2_FXP_OPENDIR:
1007		process_opendir();
1008		break;
1009	case SSH2_FXP_READDIR:
1010		process_readdir();
1011		break;
1012	case SSH2_FXP_REMOVE:
1013		process_remove();
1014		break;
1015	case SSH2_FXP_MKDIR:
1016		process_mkdir();
1017		break;
1018	case SSH2_FXP_RMDIR:
1019		process_rmdir();
1020		break;
1021	case SSH2_FXP_REALPATH:
1022		process_realpath();
1023		break;
1024	case SSH2_FXP_STAT:
1025		process_stat();
1026		break;
1027	case SSH2_FXP_RENAME:
1028		process_rename();
1029		break;
1030	case SSH2_FXP_READLINK:
1031		process_readlink();
1032		break;
1033	case SSH2_FXP_SYMLINK:
1034		process_symlink();
1035		break;
1036	case SSH2_FXP_EXTENDED:
1037		process_extended();
1038		break;
1039	default:
1040		error("Unknown message %d", type);
1041		break;
1042	}
1043	/* discard the remaining bytes from the current packet */
1044	if (buf_len < buffer_len(&iqueue))
1045		fatal("iqueue grows");
1046	consumed = buf_len - buffer_len(&iqueue);
1047	if (msg_len < consumed)
1048		fatal("msg_len %d < consumed %d", msg_len, consumed);
1049	if (msg_len > consumed)
1050		buffer_consume(&iqueue, msg_len - consumed);
1051}
1052
1053int
1054main(int ac, char **av)
1055{
1056	fd_set *rset, *wset;
1057	int in, out, max;
1058	ssize_t len, olen, set_size;
1059
1060	/* XXX should use getopt */
1061
1062	__progname = get_progname(av[0]);
1063	handle_init();
1064
1065#ifdef DEBUG_SFTP_SERVER
1066	log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
1067#endif
1068
1069	in = dup(STDIN_FILENO);
1070	out = dup(STDOUT_FILENO);
1071
1072#ifdef HAVE_CYGWIN
1073	setmode(in, O_BINARY);
1074	setmode(out, O_BINARY);
1075#endif
1076
1077	max = 0;
1078	if (in > max)
1079		max = in;
1080	if (out > max)
1081		max = out;
1082
1083	buffer_init(&iqueue);
1084	buffer_init(&oqueue);
1085
1086	set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
1087	rset = (fd_set *)xmalloc(set_size);
1088	wset = (fd_set *)xmalloc(set_size);
1089
1090	for (;;) {
1091		memset(rset, 0, set_size);
1092		memset(wset, 0, set_size);
1093
1094		FD_SET(in, rset);
1095		olen = buffer_len(&oqueue);
1096		if (olen > 0)
1097			FD_SET(out, wset);
1098
1099		if (select(max+1, rset, wset, NULL, NULL) < 0) {
1100			if (errno == EINTR)
1101				continue;
1102			exit(2);
1103		}
1104
1105		/* copy stdin to iqueue */
1106		if (FD_ISSET(in, rset)) {
1107			char buf[4*4096];
1108			len = read(in, buf, sizeof buf);
1109			if (len == 0) {
1110				debug("read eof");
1111				exit(0);
1112			} else if (len < 0) {
1113				error("read error");
1114				exit(1);
1115			} else {
1116				buffer_append(&iqueue, buf, len);
1117			}
1118		}
1119		/* send oqueue to stdout */
1120		if (FD_ISSET(out, wset)) {
1121			len = write(out, buffer_ptr(&oqueue), olen);
1122			if (len < 0) {
1123				error("write error");
1124				exit(1);
1125			} else {
1126				buffer_consume(&oqueue, len);
1127			}
1128		}
1129		/* process requests from client */
1130		process();
1131	}
1132}
1133