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