xref: /freebsd/crypto/openssh/sftp-server.c (revision 4b2eaea43fec8e8792be611dea204071a10b655a)
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"
25 RCSID("$OpenBSD: sftp-server.c,v 1.38 2002/09/11 22:41:50 djm 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
43 extern char *__progname;
44 #else
45 char *__progname;
46 #endif
47 
48 /* input and output queue */
49 Buffer iqueue;
50 Buffer oqueue;
51 
52 /* Version of client */
53 int version;
54 
55 /* portable attibutes, etc. */
56 
57 typedef struct Stat Stat;
58 
59 struct Stat {
60 	char *name;
61 	char *long_name;
62 	Attrib attrib;
63 };
64 
65 static int
66 errno_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 
96 static int
97 flags_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 
118 static Attrib *
119 get_attrib(void)
120 {
121 	return decode_attrib(&iqueue);
122 }
123 
124 /* handle handles */
125 
126 typedef struct Handle Handle;
127 struct Handle {
128 	int use;
129 	DIR *dirp;
130 	int fd;
131 	char *name;
132 };
133 
134 enum {
135 	HANDLE_UNUSED,
136 	HANDLE_DIR,
137 	HANDLE_FILE
138 };
139 
140 Handle	handles[100];
141 
142 static void
143 handle_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 
151 static int
152 handle_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 
168 static int
169 handle_is_ok(int i, int type)
170 {
171 	return i >= 0 && i < sizeof(handles)/sizeof(Handle) &&
172 	    handles[i].use == type;
173 }
174 
175 static int
176 handle_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 
186 static int
187 handle_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 
200 static char *
201 handle_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 
209 static DIR *
210 handle_to_dir(int handle)
211 {
212 	if (handle_is_ok(handle, HANDLE_DIR))
213 		return handles[handle].dirp;
214 	return NULL;
215 }
216 
217 static int
218 handle_to_fd(int handle)
219 {
220 	if (handle_is_ok(handle, HANDLE_FILE))
221 		return handles[handle].fd;
222 	return -1;
223 }
224 
225 static int
226 handle_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 
242 static int
243 get_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 
258 static void
259 send_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 
268 static void
269 send_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 }
298 static void
299 send_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 
311 static void
312 send_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 
318 static void
319 send_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 
330 static void
331 send_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 
350 static void
351 send_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 
366 static void
367 process_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 
380 static void
381 process_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 
412 static void
413 process_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 
426 static void
427 process_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 
466 static void
467 process_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 
504 static void
505 process_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 
529 static void
530 process_stat(void)
531 {
532 	process_do_stat(0);
533 }
534 
535 static void
536 process_lstat(void)
537 {
538 	process_do_stat(1);
539 }
540 
541 static void
542 process_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 
567 static struct timeval *
568 attrib_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 
579 static void
580 process_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 
615 static void
616 process_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 
669 static void
670 process_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 static void
699 process_readdir(void)
700 {
701 	DIR *dirp;
702 	struct dirent *dp;
703 	char *path;
704 	int handle;
705 	u_int32_t id;
706 
707 	id = get_int();
708 	handle = get_handle();
709 	TRACE("readdir id %u handle %d", id, handle);
710 	dirp = handle_to_dir(handle);
711 	path = handle_to_name(handle);
712 	if (dirp == NULL || path == NULL) {
713 		send_status(id, SSH2_FX_FAILURE);
714 	} else {
715 		struct stat st;
716 		char pathname[1024];
717 		Stat *stats;
718 		int nstats = 10, count = 0, i;
719 
720 		stats = xmalloc(nstats * sizeof(Stat));
721 		while ((dp = readdir(dirp)) != NULL) {
722 			if (count >= nstats) {
723 				nstats *= 2;
724 				stats = xrealloc(stats, nstats * sizeof(Stat));
725 			}
726 /* XXX OVERFLOW ? */
727 			snprintf(pathname, sizeof pathname, "%s%s%s", path,
728 			    strcmp(path, "/") ? "/" : "", dp->d_name);
729 			if (lstat(pathname, &st) < 0)
730 				continue;
731 			stat_to_attrib(&st, &(stats[count].attrib));
732 			stats[count].name = xstrdup(dp->d_name);
733 			stats[count].long_name = ls_file(dp->d_name, &st, 0);
734 			count++;
735 			/* send up to 100 entries in one message */
736 			/* XXX check packet size instead */
737 			if (count == 100)
738 				break;
739 		}
740 		if (count > 0) {
741 			send_names(id, count, stats);
742 			for (i = 0; i < count; i++) {
743 				xfree(stats[i].name);
744 				xfree(stats[i].long_name);
745 			}
746 		} else {
747 			send_status(id, SSH2_FX_EOF);
748 		}
749 		xfree(stats);
750 	}
751 }
752 
753 static void
754 process_remove(void)
755 {
756 	char *name;
757 	u_int32_t id;
758 	int status = SSH2_FX_FAILURE;
759 	int ret;
760 
761 	id = get_int();
762 	name = get_string(NULL);
763 	TRACE("remove id %u name %s", id, name);
764 	ret = unlink(name);
765 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
766 	send_status(id, status);
767 	xfree(name);
768 }
769 
770 static void
771 process_mkdir(void)
772 {
773 	Attrib *a;
774 	u_int32_t id;
775 	char *name;
776 	int ret, mode, status = SSH2_FX_FAILURE;
777 
778 	id = get_int();
779 	name = get_string(NULL);
780 	a = get_attrib();
781 	mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
782 	    a->perm & 0777 : 0777;
783 	TRACE("mkdir id %u name %s mode 0%o", id, name, mode);
784 	ret = mkdir(name, mode);
785 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
786 	send_status(id, status);
787 	xfree(name);
788 }
789 
790 static void
791 process_rmdir(void)
792 {
793 	u_int32_t id;
794 	char *name;
795 	int ret, status;
796 
797 	id = get_int();
798 	name = get_string(NULL);
799 	TRACE("rmdir id %u name %s", id, name);
800 	ret = rmdir(name);
801 	status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
802 	send_status(id, status);
803 	xfree(name);
804 }
805 
806 static void
807 process_realpath(void)
808 {
809 	char resolvedname[MAXPATHLEN];
810 	u_int32_t id;
811 	char *path;
812 
813 	id = get_int();
814 	path = get_string(NULL);
815 	if (path[0] == '\0') {
816 		xfree(path);
817 		path = xstrdup(".");
818 	}
819 	TRACE("realpath id %u path %s", id, path);
820 	if (realpath(path, resolvedname) == NULL) {
821 		send_status(id, errno_to_portable(errno));
822 	} else {
823 		Stat s;
824 		attrib_clear(&s.attrib);
825 		s.name = s.long_name = resolvedname;
826 		send_names(id, 1, &s);
827 	}
828 	xfree(path);
829 }
830 
831 static void
832 process_rename(void)
833 {
834 	u_int32_t id;
835 	struct stat st;
836 	char *oldpath, *newpath;
837 	int ret, status = SSH2_FX_FAILURE;
838 
839 	id = get_int();
840 	oldpath = get_string(NULL);
841 	newpath = get_string(NULL);
842 	TRACE("rename id %u old %s new %s", id, oldpath, newpath);
843 	/* fail if 'newpath' exists */
844 	if (stat(newpath, &st) == -1) {
845 		ret = rename(oldpath, newpath);
846 		status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
847 	}
848 	send_status(id, status);
849 	xfree(oldpath);
850 	xfree(newpath);
851 }
852 
853 static void
854 process_readlink(void)
855 {
856 	u_int32_t id;
857 	int len;
858 	char link[MAXPATHLEN];
859 	char *path;
860 
861 	id = get_int();
862 	path = get_string(NULL);
863 	TRACE("readlink id %u path %s", id, path);
864 	if ((len = readlink(path, link, sizeof(link) - 1)) == -1)
865 		send_status(id, errno_to_portable(errno));
866 	else {
867 		Stat s;
868 
869 		link[len] = '\0';
870 		attrib_clear(&s.attrib);
871 		s.name = s.long_name = link;
872 		send_names(id, 1, &s);
873 	}
874 	xfree(path);
875 }
876 
877 static void
878 process_symlink(void)
879 {
880 	u_int32_t id;
881 	struct stat st;
882 	char *oldpath, *newpath;
883 	int ret, status = SSH2_FX_FAILURE;
884 
885 	id = get_int();
886 	oldpath = get_string(NULL);
887 	newpath = get_string(NULL);
888 	TRACE("symlink id %u old %s new %s", id, oldpath, newpath);
889 	/* fail if 'newpath' exists */
890 	if (stat(newpath, &st) == -1) {
891 		ret = symlink(oldpath, newpath);
892 		status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
893 	}
894 	send_status(id, status);
895 	xfree(oldpath);
896 	xfree(newpath);
897 }
898 
899 static void
900 process_extended(void)
901 {
902 	u_int32_t id;
903 	char *request;
904 
905 	id = get_int();
906 	request = get_string(NULL);
907 	send_status(id, SSH2_FX_OP_UNSUPPORTED);		/* MUST */
908 	xfree(request);
909 }
910 
911 /* stolen from ssh-agent */
912 
913 static void
914 process(void)
915 {
916 	u_int msg_len;
917 	u_int buf_len;
918 	u_int consumed;
919 	u_int type;
920 	u_char *cp;
921 
922 	buf_len = buffer_len(&iqueue);
923 	if (buf_len < 5)
924 		return;		/* Incomplete message. */
925 	cp = buffer_ptr(&iqueue);
926 	msg_len = GET_32BIT(cp);
927 	if (msg_len > 256 * 1024) {
928 		error("bad message ");
929 		exit(11);
930 	}
931 	if (buf_len < msg_len + 4)
932 		return;
933 	buffer_consume(&iqueue, 4);
934 	buf_len -= 4;
935 	type = buffer_get_char(&iqueue);
936 	switch (type) {
937 	case SSH2_FXP_INIT:
938 		process_init();
939 		break;
940 	case SSH2_FXP_OPEN:
941 		process_open();
942 		break;
943 	case SSH2_FXP_CLOSE:
944 		process_close();
945 		break;
946 	case SSH2_FXP_READ:
947 		process_read();
948 		break;
949 	case SSH2_FXP_WRITE:
950 		process_write();
951 		break;
952 	case SSH2_FXP_LSTAT:
953 		process_lstat();
954 		break;
955 	case SSH2_FXP_FSTAT:
956 		process_fstat();
957 		break;
958 	case SSH2_FXP_SETSTAT:
959 		process_setstat();
960 		break;
961 	case SSH2_FXP_FSETSTAT:
962 		process_fsetstat();
963 		break;
964 	case SSH2_FXP_OPENDIR:
965 		process_opendir();
966 		break;
967 	case SSH2_FXP_READDIR:
968 		process_readdir();
969 		break;
970 	case SSH2_FXP_REMOVE:
971 		process_remove();
972 		break;
973 	case SSH2_FXP_MKDIR:
974 		process_mkdir();
975 		break;
976 	case SSH2_FXP_RMDIR:
977 		process_rmdir();
978 		break;
979 	case SSH2_FXP_REALPATH:
980 		process_realpath();
981 		break;
982 	case SSH2_FXP_STAT:
983 		process_stat();
984 		break;
985 	case SSH2_FXP_RENAME:
986 		process_rename();
987 		break;
988 	case SSH2_FXP_READLINK:
989 		process_readlink();
990 		break;
991 	case SSH2_FXP_SYMLINK:
992 		process_symlink();
993 		break;
994 	case SSH2_FXP_EXTENDED:
995 		process_extended();
996 		break;
997 	default:
998 		error("Unknown message %d", type);
999 		break;
1000 	}
1001 	/* discard the remaining bytes from the current packet */
1002 	if (buf_len < buffer_len(&iqueue))
1003 		fatal("iqueue grows");
1004 	consumed = buf_len - buffer_len(&iqueue);
1005 	if (msg_len < consumed)
1006 		fatal("msg_len %d < consumed %d", msg_len, consumed);
1007 	if (msg_len > consumed)
1008 		buffer_consume(&iqueue, msg_len - consumed);
1009 }
1010 
1011 int
1012 main(int ac, char **av)
1013 {
1014 	fd_set *rset, *wset;
1015 	int in, out, max;
1016 	ssize_t len, olen, set_size;
1017 
1018 	/* XXX should use getopt */
1019 
1020 	__progname = get_progname(av[0]);
1021 	handle_init();
1022 
1023 #ifdef DEBUG_SFTP_SERVER
1024 	log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
1025 #endif
1026 
1027 	in = dup(STDIN_FILENO);
1028 	out = dup(STDOUT_FILENO);
1029 
1030 #ifdef HAVE_CYGWIN
1031 	setmode(in, O_BINARY);
1032 	setmode(out, O_BINARY);
1033 #endif
1034 
1035 	max = 0;
1036 	if (in > max)
1037 		max = in;
1038 	if (out > max)
1039 		max = out;
1040 
1041 	buffer_init(&iqueue);
1042 	buffer_init(&oqueue);
1043 
1044 	set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
1045 	rset = (fd_set *)xmalloc(set_size);
1046 	wset = (fd_set *)xmalloc(set_size);
1047 
1048 	for (;;) {
1049 		memset(rset, 0, set_size);
1050 		memset(wset, 0, set_size);
1051 
1052 		FD_SET(in, rset);
1053 		olen = buffer_len(&oqueue);
1054 		if (olen > 0)
1055 			FD_SET(out, wset);
1056 
1057 		if (select(max+1, rset, wset, NULL, NULL) < 0) {
1058 			if (errno == EINTR)
1059 				continue;
1060 			exit(2);
1061 		}
1062 
1063 		/* copy stdin to iqueue */
1064 		if (FD_ISSET(in, rset)) {
1065 			char buf[4*4096];
1066 			len = read(in, buf, sizeof buf);
1067 			if (len == 0) {
1068 				debug("read eof");
1069 				exit(0);
1070 			} else if (len < 0) {
1071 				error("read error");
1072 				exit(1);
1073 			} else {
1074 				buffer_append(&iqueue, buf, len);
1075 			}
1076 		}
1077 		/* send oqueue to stdout */
1078 		if (FD_ISSET(out, wset)) {
1079 			len = write(out, buffer_ptr(&oqueue), olen);
1080 			if (len < 0) {
1081 				error("write error");
1082 				exit(1);
1083 			} else {
1084 				buffer_consume(&oqueue, len);
1085 			}
1086 		}
1087 		/* process requests from client */
1088 		process();
1089 	}
1090 }
1091