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