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