xref: /freebsd/crypto/openssh/sftp-server.c (revision c4f6a2a9e1b1879b618c436ab4f56ff75c73a0f5)
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.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
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 /*
699  * drwxr-xr-x    5 markus   markus       1024 Jan 13 18:39 .ssh
700  */
701 static char *
702 ls_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 
740 static void
741 process_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 
795 static void
796 process_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 
812 static void
813 process_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 
832 static void
833 process_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 
848 static void
849 process_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 
873 static void
874 process_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 
895 static void
896 process_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 
919 static void
920 process_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 
941 static void
942 process_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 
955 static void
956 process(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 
1053 int
1054 main(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