Annotation of src/usr.bin/ssh/sftp-server.c, Revision 1.6.2.6
1.1 markus 1: /*
1.6.2.6 ! brad 2: * Copyright (c) 2000, 2001, 2002 Markus Friedl. All rights reserved.
1.1 markus 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.2.6 ! brad 25: RCSID("$OpenBSD: sftp-server.c,v 1.33 2002/02/13 00:28:13 markus Exp $");
1.1 markus 26:
27: #include "buffer.h"
28: #include "bufaux.h"
29: #include "getput.h"
1.6.2.1 jason 30: #include "log.h"
1.1 markus 31: #include "xmalloc.h"
32:
1.6.2.1 jason 33: #include "sftp.h"
34: #include "sftp-common.h"
1.1 markus 35:
36: /* helper */
1.6.2.1 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.2.1 jason 40: #define TRACE debug
1.1 markus 41:
42: /* input and output queue */
43: Buffer iqueue;
44: Buffer oqueue;
45:
1.6.2.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.2.1 jason 53: struct Stat {
1.1 markus 54: char *name;
55: char *long_name;
56: Attrib attrib;
57: };
58:
1.6.2.5 miod 59: static int
1.2 markus 60: errno_to_portable(int unixerrno)
1.1 markus 61: {
62: int ret = 0;
1.6.2.3 jason 63:
1.2 markus 64: switch (unixerrno) {
1.1 markus 65: case 0:
1.6.2.1 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.2.1 jason 72: ret = SSH2_FX_NO_SUCH_FILE;
1.1 markus 73: break;
74: case EPERM:
75: case EACCES:
76: case EFAULT:
1.6.2.1 jason 77: ret = SSH2_FX_PERMISSION_DENIED;
1.1 markus 78: break;
79: case ENAMETOOLONG:
80: case EINVAL:
1.6.2.1 jason 81: ret = SSH2_FX_BAD_MESSAGE;
1.1 markus 82: break;
83: default:
1.6.2.1 jason 84: ret = SSH2_FX_FAILURE;
1.1 markus 85: break;
86: }
87: return ret;
88: }
89:
1.6.2.5 miod 90: static int
1.1 markus 91: flags_from_portable(int pflags)
92: {
93: int flags = 0;
1.6.2.3 jason 94:
95: if ((pflags & SSH2_FXF_READ) &&
96: (pflags & SSH2_FXF_WRITE)) {
1.1 markus 97: flags = O_RDWR;
1.6.2.1 jason 98: } else if (pflags & SSH2_FXF_READ) {
1.1 markus 99: flags = O_RDONLY;
1.6.2.1 jason 100: } else if (pflags & SSH2_FXF_WRITE) {
1.1 markus 101: flags = O_WRONLY;
102: }
1.6.2.1 jason 103: if (pflags & SSH2_FXF_CREAT)
1.1 markus 104: flags |= O_CREAT;
1.6.2.1 jason 105: if (pflags & SSH2_FXF_TRUNC)
1.1 markus 106: flags |= O_TRUNC;
1.6.2.1 jason 107: if (pflags & SSH2_FXF_EXCL)
1.1 markus 108: flags |= O_EXCL;
109: return flags;
110: }
111:
1.6.2.5 miod 112: static Attrib *
1.1 markus 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.2.3 jason 127:
1.1 markus 128: enum {
129: HANDLE_UNUSED,
130: HANDLE_DIR,
131: HANDLE_FILE
132: };
1.6.2.3 jason 133:
1.1 markus 134: Handle handles[100];
135:
1.6.2.5 miod 136: static void
1.1 markus 137: handle_init(void)
138: {
139: int i;
1.6.2.3 jason 140:
1.6.2.6 ! brad 141: for (i = 0; i < sizeof(handles)/sizeof(Handle); i++)
1.1 markus 142: handles[i].use = HANDLE_UNUSED;
143: }
144:
1.6.2.5 miod 145: static int
1.1 markus 146: handle_new(int use, char *name, int fd, DIR *dirp)
147: {
148: int i;
1.6.2.3 jason 149:
1.6.2.6 ! brad 150: for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
1.1 markus 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:
1.6.2.5 miod 162: static int
1.1 markus 163: handle_is_ok(int i, int type)
164: {
1.6.2.1 jason 165: return i >= 0 && i < sizeof(handles)/sizeof(Handle) &&
166: handles[i].use == type;
1.1 markus 167: }
168:
1.6.2.5 miod 169: static int
1.1 markus 170: handle_to_string(int handle, char **stringp, int *hlenp)
171: {
172: if (stringp == NULL || hlenp == NULL)
173: return -1;
1.6.2.1 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:
1.6.2.5 miod 180: static int
1.5 markus 181: handle_from_string(char *handle, u_int hlen)
1.1 markus 182: {
1.6.2.1 jason 183: int val;
1.6.2.3 jason 184:
1.6.2.1 jason 185: if (hlen != sizeof(int32_t))
1.1 markus 186: return -1;
1.6.2.1 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:
1.6.2.5 miod 194: static char *
1.1 markus 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:
1.6.2.5 miod 203: static DIR *
1.1 markus 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:
1.6.2.5 miod 211: static int
1.1 markus 212: handle_to_fd(int handle)
213: {
1.6.2.1 jason 214: if (handle_is_ok(handle, HANDLE_FILE))
1.1 markus 215: return handles[handle].fd;
216: return -1;
217: }
218:
1.6.2.5 miod 219: static int
1.1 markus 220: handle_close(int handle)
221: {
222: int ret = -1;
1.6.2.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:
1.6.2.5 miod 236: static int
1.1 markus 237: get_handle(void)
238: {
239: char *handle;
1.6.2.1 jason 240: int val = -1;
1.5 markus 241: u_int hlen;
1.6.2.3 jason 242:
1.1 markus 243: handle = get_string(&hlen);
1.6.2.1 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:
1.6.2.5 miod 252: static void
1.1 markus 253: send_msg(Buffer *m)
254: {
255: int mlen = buffer_len(m);
1.6.2.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:
1.6.2.5 miod 262: static void
1.1 markus 263: send_status(u_int32_t id, u_int32_t error)
264: {
265: Buffer msg;
1.6.2.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.2.1 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.2.3 jason 284: if (version >= 3) {
1.6.2.4 jason 285: buffer_put_cstring(&msg,
1.6.2.3 jason 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: }
1.6.2.5 miod 292: static void
1.1 markus 293: send_data_or_handle(char type, u_int32_t id, char *data, int dlen)
294: {
295: Buffer msg;
1.6.2.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:
1.6.2.5 miod 305: static void
1.1 markus 306: send_data(u_int32_t id, char *data, int dlen)
307: {
308: TRACE("sent data id %d len %d", id, dlen);
1.6.2.1 jason 309: send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
1.1 markus 310: }
311:
1.6.2.5 miod 312: static void
1.1 markus 313: send_handle(u_int32_t id, int handle)
314: {
315: char *string;
316: int hlen;
1.6.2.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.2.1 jason 320: send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
1.1 markus 321: xfree(string);
322: }
323:
1.6.2.5 miod 324: static void
1.1 markus 325: send_names(u_int32_t id, int count, Stat *stats)
326: {
327: Buffer msg;
328: int i;
1.6.2.3 jason 329:
1.1 markus 330: buffer_init(&msg);
1.6.2.1 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:
1.6.2.5 miod 344: static void
1.1 markus 345: send_attrib(u_int32_t id, Attrib *a)
346: {
347: Buffer msg;
1.6.2.3 jason 348:
1.1 markus 349: TRACE("sent attrib id %d have 0x%x", id, a->flags);
350: buffer_init(&msg);
1.6.2.1 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:
1.6.2.5 miod 360: static void
1.1 markus 361: process_init(void)
362: {
363: Buffer msg;
364:
1.6.2.3 jason 365: version = buffer_get_int(&iqueue);
1.1 markus 366: TRACE("client version %d", version);
367: buffer_init(&msg);
1.6.2.1 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:
1.6.2.5 miod 374: static void
1.1 markus 375: process_open(void)
376: {
377: u_int32_t id, pflags;
378: Attrib *a;
379: char *name;
1.6.2.1 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.2.1 jason 384: pflags = get_int(); /* portable flags */
1.1 markus 385: a = get_attrib();
386: flags = flags_from_portable(pflags);
1.6.2.1 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.2.1 jason 398: status = SSH2_FX_OK;
1.1 markus 399: }
400: }
1.6.2.1 jason 401: if (status != SSH2_FX_OK)
1.1 markus 402: send_status(id, status);
403: xfree(name);
404: }
405:
1.6.2.5 miod 406: static void
1.1 markus 407: process_close(void)
408: {
409: u_int32_t id;
1.6.2.1 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.2.1 jason 416: status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1.1 markus 417: send_status(id, status);
418: }
419:
1.6.2.5 miod 420: static void
1.1 markus 421: process_read(void)
422: {
423: char buf[64*1024];
1.6.2.1 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.2.1 jason 430: off = get_int64();
1.1 markus 431: len = get_int();
432:
1.6.2.1 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.2.1 jason 449: status = SSH2_FX_EOF;
1.1 markus 450: } else {
451: send_data(id, buf, ret);
1.6.2.1 jason 452: status = SSH2_FX_OK;
1.1 markus 453: }
454: }
455: }
1.6.2.1 jason 456: if (status != SSH2_FX_OK)
1.1 markus 457: send_status(id, status);
458: }
459:
1.6.2.5 miod 460: static void
1.1 markus 461: process_write(void)
462: {
1.6.2.1 jason 463: u_int32_t id;
1.1 markus 464: u_int64_t off;
1.5 markus 465: u_int len;
1.6.2.1 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.2.1 jason 471: off = get_int64();
1.1 markus 472: data = get_string(&len);
473:
1.6.2.1 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.2.1 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:
1.6.2.5 miod 498: static void
1.1 markus 499: process_do_stat(int do_lstat)
500: {
1.6.2.1 jason 501: Attrib a;
1.1 markus 502: struct stat st;
503: u_int32_t id;
504: char *name;
1.6.2.1 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.2.1 jason 514: stat_to_attrib(&st, &a);
515: send_attrib(id, &a);
516: status = SSH2_FX_OK;
1.1 markus 517: }
1.6.2.1 jason 518: if (status != SSH2_FX_OK)
1.1 markus 519: send_status(id, status);
520: xfree(name);
521: }
522:
1.6.2.5 miod 523: static void
1.1 markus 524: process_stat(void)
525: {
526: process_do_stat(0);
527: }
528:
1.6.2.5 miod 529: static void
1.1 markus 530: process_lstat(void)
531: {
532: process_do_stat(1);
533: }
534:
1.6.2.5 miod 535: static void
1.1 markus 536: process_fstat(void)
537: {
1.6.2.1 jason 538: Attrib a;
1.1 markus 539: struct stat st;
540: u_int32_t id;
1.6.2.1 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.2.1 jason 552: stat_to_attrib(&st, &a);
553: send_attrib(id, &a);
554: status = SSH2_FX_OK;
1.1 markus 555: }
556: }
1.6.2.1 jason 557: if (status != SSH2_FX_OK)
1.1 markus 558: send_status(id, status);
559: }
560:
1.6.2.5 miod 561: static struct timeval *
1.1 markus 562: attrib_to_tv(Attrib *a)
563: {
564: static struct timeval tv[2];
1.6.2.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:
1.6.2.5 miod 573: static void
1.1 markus 574: process_setstat(void)
575: {
576: Attrib *a;
577: u_int32_t id;
578: char *name;
579: int ret;
1.6.2.1 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.2.6 ! brad 586: if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
! 587: ret = truncate(name, a->size);
! 588: if (ret == -1)
! 589: status = errno_to_portable(errno);
! 590: }
1.6.2.1 jason 591: if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
1.1 markus 592: ret = chmod(name, a->perm & 0777);
593: if (ret == -1)
594: status = errno_to_portable(errno);
595: }
1.6.2.1 jason 596: if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
1.1 markus 597: ret = utimes(name, attrib_to_tv(a));
598: if (ret == -1)
599: status = errno_to_portable(errno);
600: }
1.6.2.1 jason 601: if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
602: ret = chown(name, a->uid, a->gid);
603: if (ret == -1)
604: status = errno_to_portable(errno);
605: }
1.1 markus 606: send_status(id, status);
607: xfree(name);
608: }
609:
1.6.2.5 miod 610: static void
1.1 markus 611: process_fsetstat(void)
612: {
613: Attrib *a;
614: u_int32_t id;
615: int handle, fd, ret;
1.6.2.1 jason 616: int status = SSH2_FX_OK;
1.1 markus 617:
618: id = get_int();
619: handle = get_handle();
620: a = get_attrib();
621: TRACE("fsetstat id %d handle %d", id, handle);
622: fd = handle_to_fd(handle);
623: if (fd < 0) {
1.6.2.1 jason 624: status = SSH2_FX_FAILURE;
1.1 markus 625: } else {
1.6.2.6 ! brad 626: if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
! 627: ret = ftruncate(fd, a->size);
! 628: if (ret == -1)
! 629: status = errno_to_portable(errno);
! 630: }
1.6.2.1 jason 631: if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
1.1 markus 632: ret = fchmod(fd, a->perm & 0777);
633: if (ret == -1)
634: status = errno_to_portable(errno);
635: }
1.6.2.1 jason 636: if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
1.1 markus 637: ret = futimes(fd, attrib_to_tv(a));
638: if (ret == -1)
639: status = errno_to_portable(errno);
640: }
1.6.2.1 jason 641: if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
642: ret = fchown(fd, a->uid, a->gid);
643: if (ret == -1)
644: status = errno_to_portable(errno);
645: }
1.1 markus 646: }
647: send_status(id, status);
648: }
649:
1.6.2.5 miod 650: static void
1.1 markus 651: process_opendir(void)
652: {
653: DIR *dirp = NULL;
654: char *path;
1.6.2.1 jason 655: int handle, status = SSH2_FX_FAILURE;
1.1 markus 656: u_int32_t id;
657:
658: id = get_int();
659: path = get_string(NULL);
660: TRACE("opendir id %d path %s", id, path);
1.6.2.1 jason 661: dirp = opendir(path);
1.1 markus 662: if (dirp == NULL) {
663: status = errno_to_portable(errno);
664: } else {
665: handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp);
666: if (handle < 0) {
667: closedir(dirp);
668: } else {
669: send_handle(id, handle);
1.6.2.1 jason 670: status = SSH2_FX_OK;
1.1 markus 671: }
1.6.2.1 jason 672:
1.1 markus 673: }
1.6.2.1 jason 674: if (status != SSH2_FX_OK)
1.1 markus 675: send_status(id, status);
676: xfree(path);
677: }
678:
1.6.2.1 jason 679: /*
680: * drwxr-xr-x 5 markus markus 1024 Jan 13 18:39 .ssh
681: */
1.6.2.5 miod 682: static char *
1.1 markus 683: ls_file(char *name, struct stat *st)
684: {
1.6.2.5 miod 685: int ulen, glen, sz = 0;
1.6.2.1 jason 686: struct passwd *pw;
687: struct group *gr;
688: struct tm *ltime = localtime(&st->st_mtime);
689: char *user, *group;
690: char buf[1024], mode[11+1], tbuf[12+1], ubuf[11+1], gbuf[11+1];
691:
692: strmode(st->st_mode, mode);
693: if ((pw = getpwuid(st->st_uid)) != NULL) {
694: user = pw->pw_name;
695: } else {
696: snprintf(ubuf, sizeof ubuf, "%d", st->st_uid);
697: user = ubuf;
698: }
699: if ((gr = getgrgid(st->st_gid)) != NULL) {
700: group = gr->gr_name;
701: } else {
702: snprintf(gbuf, sizeof gbuf, "%d", st->st_gid);
703: group = gbuf;
704: }
705: if (ltime != NULL) {
706: if (time(NULL) - st->st_mtime < (365*24*60*60)/2)
707: sz = strftime(tbuf, sizeof tbuf, "%b %e %H:%M", ltime);
708: else
709: sz = strftime(tbuf, sizeof tbuf, "%b %e %Y", ltime);
710: }
711: if (sz == 0)
712: tbuf[0] = '\0';
1.6.2.5 miod 713: ulen = MAX(strlen(user), 8);
714: glen = MAX(strlen(group), 8);
715: snprintf(buf, sizeof buf, "%s %3d %-*s %-*s %8llu %s %s", mode,
716: st->st_nlink, ulen, user, glen, group,
717: (unsigned long long)st->st_size, tbuf, name);
1.1 markus 718: return xstrdup(buf);
719: }
720:
1.6.2.5 miod 721: static void
1.1 markus 722: process_readdir(void)
723: {
724: DIR *dirp;
725: struct dirent *dp;
726: char *path;
727: int handle;
728: u_int32_t id;
729:
730: id = get_int();
731: handle = get_handle();
732: TRACE("readdir id %d handle %d", id, handle);
733: dirp = handle_to_dir(handle);
734: path = handle_to_name(handle);
735: if (dirp == NULL || path == NULL) {
1.6.2.1 jason 736: send_status(id, SSH2_FX_FAILURE);
1.1 markus 737: } else {
738: struct stat st;
739: char pathname[1024];
740: Stat *stats;
741: int nstats = 10, count = 0, i;
742: stats = xmalloc(nstats * sizeof(Stat));
743: while ((dp = readdir(dirp)) != NULL) {
744: if (count >= nstats) {
745: nstats *= 2;
746: stats = xrealloc(stats, nstats * sizeof(Stat));
747: }
748: /* XXX OVERFLOW ? */
1.6.2.5 miod 749: snprintf(pathname, sizeof pathname, "%s%s%s", path,
750: strcmp(path, "/") ? "/" : "", dp->d_name);
1.1 markus 751: if (lstat(pathname, &st) < 0)
752: continue;
1.6.2.1 jason 753: stat_to_attrib(&st, &(stats[count].attrib));
1.1 markus 754: stats[count].name = xstrdup(dp->d_name);
755: stats[count].long_name = ls_file(dp->d_name, &st);
756: count++;
757: /* send up to 100 entries in one message */
1.6.2.1 jason 758: /* XXX check packet size instead */
1.1 markus 759: if (count == 100)
760: break;
761: }
1.6.2.1 jason 762: if (count > 0) {
763: send_names(id, count, stats);
1.6.2.6 ! brad 764: for (i = 0; i < count; i++) {
1.6.2.1 jason 765: xfree(stats[i].name);
766: xfree(stats[i].long_name);
767: }
768: } else {
769: send_status(id, SSH2_FX_EOF);
1.1 markus 770: }
771: xfree(stats);
772: }
773: }
774:
1.6.2.5 miod 775: static void
1.1 markus 776: process_remove(void)
777: {
778: char *name;
779: u_int32_t id;
1.6.2.1 jason 780: int status = SSH2_FX_FAILURE;
1.1 markus 781: int ret;
782:
783: id = get_int();
784: name = get_string(NULL);
785: TRACE("remove id %d name %s", id, name);
1.6.2.1 jason 786: ret = unlink(name);
787: status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1.1 markus 788: send_status(id, status);
789: xfree(name);
790: }
791:
1.6.2.5 miod 792: static void
1.1 markus 793: process_mkdir(void)
794: {
795: Attrib *a;
796: u_int32_t id;
797: char *name;
1.6.2.1 jason 798: int ret, mode, status = SSH2_FX_FAILURE;
1.1 markus 799:
800: id = get_int();
801: name = get_string(NULL);
802: a = get_attrib();
1.6.2.1 jason 803: mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
804: a->perm & 0777 : 0777;
1.1 markus 805: TRACE("mkdir id %d name %s mode 0%o", id, name, mode);
806: ret = mkdir(name, mode);
1.6.2.1 jason 807: status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1.1 markus 808: send_status(id, status);
809: xfree(name);
810: }
811:
1.6.2.5 miod 812: static void
1.1 markus 813: process_rmdir(void)
814: {
815: u_int32_t id;
816: char *name;
817: int ret, status;
818:
819: id = get_int();
820: name = get_string(NULL);
821: TRACE("rmdir id %d name %s", id, name);
822: ret = rmdir(name);
1.6.2.1 jason 823: status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1.1 markus 824: send_status(id, status);
825: xfree(name);
826: }
827:
1.6.2.5 miod 828: static void
1.1 markus 829: process_realpath(void)
830: {
831: char resolvedname[MAXPATHLEN];
832: u_int32_t id;
833: char *path;
834:
835: id = get_int();
836: path = get_string(NULL);
1.6.2.1 jason 837: if (path[0] == '\0') {
838: xfree(path);
839: path = xstrdup(".");
840: }
1.1 markus 841: TRACE("realpath id %d path %s", id, path);
842: if (realpath(path, resolvedname) == NULL) {
843: send_status(id, errno_to_portable(errno));
844: } else {
845: Stat s;
846: attrib_clear(&s.attrib);
847: s.name = s.long_name = resolvedname;
848: send_names(id, 1, &s);
849: }
850: xfree(path);
851: }
852:
1.6.2.5 miod 853: static void
1.1 markus 854: process_rename(void)
855: {
856: u_int32_t id;
1.6.2.1 jason 857: struct stat st;
1.1 markus 858: char *oldpath, *newpath;
1.6.2.1 jason 859: int ret, status = SSH2_FX_FAILURE;
1.1 markus 860:
861: id = get_int();
862: oldpath = get_string(NULL);
863: newpath = get_string(NULL);
864: TRACE("rename id %d old %s new %s", id, oldpath, newpath);
1.6.2.1 jason 865: /* fail if 'newpath' exists */
866: if (stat(newpath, &st) == -1) {
867: ret = rename(oldpath, newpath);
868: status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
869: }
1.1 markus 870: send_status(id, status);
871: xfree(oldpath);
872: xfree(newpath);
873: }
874:
1.6.2.5 miod 875: static void
1.6.2.3 jason 876: process_readlink(void)
877: {
878: u_int32_t id;
1.6.2.5 miod 879: int len;
1.6.2.3 jason 880: char link[MAXPATHLEN];
881: char *path;
882:
883: id = get_int();
884: path = get_string(NULL);
885: TRACE("readlink id %d path %s", id, path);
1.6.2.5 miod 886: if ((len = readlink(path, link, sizeof(link) - 1)) == -1)
1.6.2.3 jason 887: send_status(id, errno_to_portable(errno));
888: else {
889: Stat s;
1.6.2.6 ! brad 890:
1.6.2.5 miod 891: link[len] = '\0';
1.6.2.3 jason 892: attrib_clear(&s.attrib);
893: s.name = s.long_name = link;
894: send_names(id, 1, &s);
895: }
896: xfree(path);
897: }
898:
1.6.2.5 miod 899: static void
1.6.2.3 jason 900: process_symlink(void)
901: {
902: u_int32_t id;
903: struct stat st;
904: char *oldpath, *newpath;
905: int ret, status = SSH2_FX_FAILURE;
906:
907: id = get_int();
908: oldpath = get_string(NULL);
909: newpath = get_string(NULL);
910: TRACE("symlink id %d old %s new %s", id, oldpath, newpath);
911: /* fail if 'newpath' exists */
912: if (stat(newpath, &st) == -1) {
913: ret = symlink(oldpath, newpath);
914: status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
915: }
916: send_status(id, status);
917: xfree(oldpath);
918: xfree(newpath);
919: }
920:
1.6.2.5 miod 921: static void
1.6.2.1 jason 922: process_extended(void)
923: {
924: u_int32_t id;
925: char *request;
926:
927: id = get_int();
928: request = get_string(NULL);
929: send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
930: xfree(request);
931: }
1.1 markus 932:
933: /* stolen from ssh-agent */
934:
1.6.2.5 miod 935: static void
1.1 markus 936: process(void)
937: {
1.6.2.1 jason 938: u_int msg_len;
939: u_int type;
940: u_char *cp;
1.1 markus 941:
942: if (buffer_len(&iqueue) < 5)
943: return; /* Incomplete message. */
1.6.2.6 ! brad 944: cp = buffer_ptr(&iqueue);
1.1 markus 945: msg_len = GET_32BIT(cp);
946: if (msg_len > 256 * 1024) {
947: error("bad message ");
948: exit(11);
949: }
950: if (buffer_len(&iqueue) < msg_len + 4)
951: return;
952: buffer_consume(&iqueue, 4);
953: type = buffer_get_char(&iqueue);
954: switch (type) {
1.6.2.1 jason 955: case SSH2_FXP_INIT:
1.1 markus 956: process_init();
957: break;
1.6.2.1 jason 958: case SSH2_FXP_OPEN:
1.1 markus 959: process_open();
960: break;
1.6.2.1 jason 961: case SSH2_FXP_CLOSE:
1.1 markus 962: process_close();
963: break;
1.6.2.1 jason 964: case SSH2_FXP_READ:
1.1 markus 965: process_read();
966: break;
1.6.2.1 jason 967: case SSH2_FXP_WRITE:
1.1 markus 968: process_write();
969: break;
1.6.2.1 jason 970: case SSH2_FXP_LSTAT:
1.1 markus 971: process_lstat();
972: break;
1.6.2.1 jason 973: case SSH2_FXP_FSTAT:
1.1 markus 974: process_fstat();
975: break;
1.6.2.1 jason 976: case SSH2_FXP_SETSTAT:
1.1 markus 977: process_setstat();
978: break;
1.6.2.1 jason 979: case SSH2_FXP_FSETSTAT:
1.1 markus 980: process_fsetstat();
981: break;
1.6.2.1 jason 982: case SSH2_FXP_OPENDIR:
1.1 markus 983: process_opendir();
984: break;
1.6.2.1 jason 985: case SSH2_FXP_READDIR:
1.1 markus 986: process_readdir();
987: break;
1.6.2.1 jason 988: case SSH2_FXP_REMOVE:
1.1 markus 989: process_remove();
990: break;
1.6.2.1 jason 991: case SSH2_FXP_MKDIR:
1.1 markus 992: process_mkdir();
993: break;
1.6.2.1 jason 994: case SSH2_FXP_RMDIR:
1.1 markus 995: process_rmdir();
996: break;
1.6.2.1 jason 997: case SSH2_FXP_REALPATH:
1.1 markus 998: process_realpath();
999: break;
1.6.2.1 jason 1000: case SSH2_FXP_STAT:
1.1 markus 1001: process_stat();
1002: break;
1.6.2.1 jason 1003: case SSH2_FXP_RENAME:
1.1 markus 1004: process_rename();
1005: break;
1.6.2.3 jason 1006: case SSH2_FXP_READLINK:
1007: process_readlink();
1008: break;
1009: case SSH2_FXP_SYMLINK:
1010: process_symlink();
1011: break;
1.6.2.1 jason 1012: case SSH2_FXP_EXTENDED:
1013: process_extended();
1014: break;
1.1 markus 1015: default:
1016: error("Unknown message %d", type);
1017: break;
1018: }
1019: }
1020:
1021: int
1022: main(int ac, char **av)
1023: {
1.6.2.3 jason 1024: fd_set *rset, *wset;
1.1 markus 1025: int in, out, max;
1.6.2.3 jason 1026: ssize_t len, olen, set_size;
1027:
1028: /* XXX should use getopt */
1.1 markus 1029:
1030: handle_init();
1.6.2.1 jason 1031:
1032: #ifdef DEBUG_SFTP_SERVER
1033: log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
1034: #endif
1.1 markus 1035:
1036: in = dup(STDIN_FILENO);
1037: out = dup(STDOUT_FILENO);
1038:
1039: max = 0;
1040: if (in > max)
1041: max = in;
1042: if (out > max)
1043: max = out;
1044:
1045: buffer_init(&iqueue);
1046: buffer_init(&oqueue);
1047:
1.6.2.3 jason 1048: set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
1049: rset = (fd_set *)xmalloc(set_size);
1050: wset = (fd_set *)xmalloc(set_size);
1051:
1.1 markus 1052: for (;;) {
1.6.2.3 jason 1053: memset(rset, 0, set_size);
1054: memset(wset, 0, set_size);
1.1 markus 1055:
1.6.2.3 jason 1056: FD_SET(in, rset);
1.1 markus 1057: olen = buffer_len(&oqueue);
1058: if (olen > 0)
1.6.2.3 jason 1059: FD_SET(out, wset);
1.1 markus 1060:
1.6.2.3 jason 1061: if (select(max+1, rset, wset, NULL, NULL) < 0) {
1.1 markus 1062: if (errno == EINTR)
1063: continue;
1064: exit(2);
1065: }
1066:
1067: /* copy stdin to iqueue */
1.6.2.3 jason 1068: if (FD_ISSET(in, rset)) {
1.1 markus 1069: char buf[4*4096];
1070: len = read(in, buf, sizeof buf);
1071: if (len == 0) {
1072: debug("read eof");
1073: exit(0);
1074: } else if (len < 0) {
1075: error("read error");
1076: exit(1);
1077: } else {
1078: buffer_append(&iqueue, buf, len);
1079: }
1080: }
1081: /* send oqueue to stdout */
1.6.2.3 jason 1082: if (FD_ISSET(out, wset)) {
1.1 markus 1083: len = write(out, buffer_ptr(&oqueue), olen);
1084: if (len < 0) {
1085: error("write error");
1086: exit(1);
1087: } else {
1088: buffer_consume(&oqueue, len);
1089: }
1090: }
1091: /* process requests from client */
1092: process();
1093: }
1094: }