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