blob: e4432ca518fcf4783d6708aaa8ba76fd421f3619 [file] [log] [blame]
Damien Miller7b28dc52000-09-05 13:34:53 +11001/*
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.
Damien Miller7b28dc52000-09-05 13:34:53 +110012 *
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"
Ben Lindstrom2f959b42001-01-11 06:20:23 +000025RCSID("$OpenBSD: sftp-server.c,v 1.10 2001/01/10 22:56:22 markus Exp $");
Damien Miller7b28dc52000-09-05 13:34:53 +110026
27#include "ssh.h"
28#include "buffer.h"
29#include "bufaux.h"
30#include "getput.h"
31#include "xmalloc.h"
32
Ben Lindstrom2f959b42001-01-11 06:20:23 +000033#include "sftp.h"
Damien Miller7b28dc52000-09-05 13:34:53 +110034
35/* helper */
Ben Lindstrom2f959b42001-01-11 06:20:23 +000036#define get_int64() buffer_get_int64(&iqueue);
Damien Miller7b28dc52000-09-05 13:34:53 +110037#define get_int() buffer_get_int(&iqueue);
38#define get_string(lenp) buffer_get_string(&iqueue, lenp);
Ben Lindstrom2f959b42001-01-11 06:20:23 +000039#define TRACE debug
Damien Miller7b28dc52000-09-05 13:34:53 +110040
Ben Lindstrom49a79c02000-11-17 03:47:20 +000041#ifdef HAVE___PROGNAME
42extern char *__progname;
43#else
44char *__progname;
45#endif
46
Damien Miller7b28dc52000-09-05 13:34:53 +110047/* input and output queue */
48Buffer iqueue;
49Buffer oqueue;
50
51/* portable attibutes, etc. */
52
53typedef struct Attrib Attrib;
54typedef struct Stat Stat;
55
56struct Attrib
57{
58 u_int32_t flags;
Damien Miller7b28dc52000-09-05 13:34:53 +110059 u_int64_t size;
60 u_int32_t uid;
61 u_int32_t gid;
62 u_int32_t perm;
63 u_int32_t atime;
64 u_int32_t mtime;
65};
66
67struct Stat
68{
69 char *name;
70 char *long_name;
71 Attrib attrib;
72};
73
74int
75errno_to_portable(int unixerrno)
76{
77 int ret = 0;
78 switch (unixerrno) {
79 case 0:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000080 ret = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +110081 break;
82 case ENOENT:
83 case ENOTDIR:
84 case EBADF:
85 case ELOOP:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000086 ret = SSH2_FX_NO_SUCH_FILE;
Damien Miller7b28dc52000-09-05 13:34:53 +110087 break;
88 case EPERM:
89 case EACCES:
90 case EFAULT:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000091 ret = SSH2_FX_PERMISSION_DENIED;
Damien Miller7b28dc52000-09-05 13:34:53 +110092 break;
93 case ENAMETOOLONG:
94 case EINVAL:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000095 ret = SSH2_FX_BAD_MESSAGE;
Damien Miller7b28dc52000-09-05 13:34:53 +110096 break;
97 default:
Ben Lindstrom2f959b42001-01-11 06:20:23 +000098 ret = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +110099 break;
100 }
101 return ret;
102}
103
104int
105flags_from_portable(int pflags)
106{
107 int flags = 0;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000108 if (pflags & SSH2_FXF_READ &&
109 pflags & SSH2_FXF_WRITE) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100110 flags = O_RDWR;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000111 } else if (pflags & SSH2_FXF_READ) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100112 flags = O_RDONLY;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000113 } else if (pflags & SSH2_FXF_WRITE) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100114 flags = O_WRONLY;
115 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000116 if (pflags & SSH2_FXF_CREAT)
Damien Miller7b28dc52000-09-05 13:34:53 +1100117 flags |= O_CREAT;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000118 if (pflags & SSH2_FXF_TRUNC)
Damien Miller7b28dc52000-09-05 13:34:53 +1100119 flags |= O_TRUNC;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000120 if (pflags & SSH2_FXF_EXCL)
Damien Miller7b28dc52000-09-05 13:34:53 +1100121 flags |= O_EXCL;
122 return flags;
123}
124
125void
126attrib_clear(Attrib *a)
127{
128 a->flags = 0;
Damien Miller7b28dc52000-09-05 13:34:53 +1100129 a->size = 0;
130 a->uid = 0;
131 a->gid = 0;
132 a->perm = 0;
133 a->atime = 0;
134 a->mtime = 0;
135}
136
137Attrib *
138decode_attrib(Buffer *b)
139{
140 static Attrib a;
141 attrib_clear(&a);
Damien Millere4340be2000-09-16 13:29:08 +1100142 a.flags = buffer_get_int(b);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000143 if (a.flags & SSH2_FILEXFER_ATTR_SIZE) {
144 a.size = buffer_get_int64(b);
Damien Miller7b28dc52000-09-05 13:34:53 +1100145 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000146 if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) {
Damien Millere4340be2000-09-16 13:29:08 +1100147 a.uid = buffer_get_int(b);
148 a.gid = buffer_get_int(b);
Damien Miller7b28dc52000-09-05 13:34:53 +1100149 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000150 if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Damien Millere4340be2000-09-16 13:29:08 +1100151 a.perm = buffer_get_int(b);
Damien Miller7b28dc52000-09-05 13:34:53 +1100152 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000153 if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Millere4340be2000-09-16 13:29:08 +1100154 a.atime = buffer_get_int(b);
155 a.mtime = buffer_get_int(b);
Damien Miller7b28dc52000-09-05 13:34:53 +1100156 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000157 /* vendor-specific extensions */
158 if (a.flags & SSH2_FILEXFER_ATTR_EXTENDED) {
159 char *type, *data;
160 int i, count;
161 count = buffer_get_int(b);
162 for (i = 0; i < count; i++) {
163 type = buffer_get_string(b, NULL);
164 data = buffer_get_string(b, NULL);
165 xfree(type);
166 xfree(data);
167 }
168 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100169 return &a;
170}
171
172void
173encode_attrib(Buffer *b, Attrib *a)
174{
175 buffer_put_int(b, a->flags);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000176 if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
177 buffer_put_int64(b, a->size);
Damien Miller7b28dc52000-09-05 13:34:53 +1100178 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000179 if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100180 buffer_put_int(b, a->uid);
181 buffer_put_int(b, a->gid);
182 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000183 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100184 buffer_put_int(b, a->perm);
185 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000186 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100187 buffer_put_int(b, a->atime);
188 buffer_put_int(b, a->mtime);
189 }
190}
191
192Attrib *
193stat_to_attrib(struct stat *st)
194{
195 static Attrib a;
196 attrib_clear(&a);
197 a.flags = 0;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000198 a.flags |= SSH2_FILEXFER_ATTR_SIZE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100199 a.size = st->st_size;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000200 a.flags |= SSH2_FILEXFER_ATTR_UIDGID;
Damien Miller7b28dc52000-09-05 13:34:53 +1100201 a.uid = st->st_uid;
202 a.gid = st->st_gid;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000203 a.flags |= SSH2_FILEXFER_ATTR_PERMISSIONS;
Damien Miller7b28dc52000-09-05 13:34:53 +1100204 a.perm = st->st_mode;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000205 a.flags |= SSH2_FILEXFER_ATTR_ACMODTIME;
Damien Miller7b28dc52000-09-05 13:34:53 +1100206 a.atime = st->st_atime;
207 a.mtime = st->st_mtime;
208 return &a;
209}
210
211Attrib *
212get_attrib(void)
213{
214 return decode_attrib(&iqueue);
215}
216
217/* handle handles */
218
219typedef struct Handle Handle;
220struct Handle {
221 int use;
222 DIR *dirp;
223 int fd;
224 char *name;
225};
226enum {
227 HANDLE_UNUSED,
228 HANDLE_DIR,
229 HANDLE_FILE
230};
231Handle handles[100];
232
233void
234handle_init(void)
235{
236 int i;
237 for(i = 0; i < sizeof(handles)/sizeof(Handle); i++)
238 handles[i].use = HANDLE_UNUSED;
239}
240
241int
242handle_new(int use, char *name, int fd, DIR *dirp)
243{
244 int i;
245 for(i = 0; i < sizeof(handles)/sizeof(Handle); i++) {
246 if (handles[i].use == HANDLE_UNUSED) {
247 handles[i].use = use;
248 handles[i].dirp = dirp;
249 handles[i].fd = fd;
250 handles[i].name = name;
251 return i;
252 }
253 }
254 return -1;
255}
256
257int
258handle_is_ok(int i, int type)
259{
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000260 return i >= 0 && i < sizeof(handles)/sizeof(Handle) &&
261 handles[i].use == type;
Damien Miller7b28dc52000-09-05 13:34:53 +1100262}
263
264int
265handle_to_string(int handle, char **stringp, int *hlenp)
266{
267 char buf[1024];
268 if (stringp == NULL || hlenp == NULL)
269 return -1;
270 snprintf(buf, sizeof buf, "%d", handle);
271 *stringp = xstrdup(buf);
272 *hlenp = strlen(*stringp);
273 return 0;
274}
275
276int
Damien Millere4340be2000-09-16 13:29:08 +1100277handle_from_string(char *handle, u_int hlen)
Damien Miller7b28dc52000-09-05 13:34:53 +1100278{
279/* XXX OVERFLOW ? */
280 char *ep;
281 long lval = strtol(handle, &ep, 10);
282 int val = lval;
283 if (*ep != '\0')
284 return -1;
285 if (handle_is_ok(val, HANDLE_FILE) ||
286 handle_is_ok(val, HANDLE_DIR))
287 return val;
288 return -1;
289}
290
291char *
292handle_to_name(int handle)
293{
294 if (handle_is_ok(handle, HANDLE_DIR)||
295 handle_is_ok(handle, HANDLE_FILE))
296 return handles[handle].name;
297 return NULL;
298}
299
300DIR *
301handle_to_dir(int handle)
302{
303 if (handle_is_ok(handle, HANDLE_DIR))
304 return handles[handle].dirp;
305 return NULL;
306}
307
308int
309handle_to_fd(int handle)
310{
311 if (handle_is_ok(handle, HANDLE_FILE))
312 return handles[handle].fd;
313 return -1;
314}
315
316int
317handle_close(int handle)
318{
319 int ret = -1;
320 if (handle_is_ok(handle, HANDLE_FILE)) {
321 ret = close(handles[handle].fd);
322 handles[handle].use = HANDLE_UNUSED;
323 } else if (handle_is_ok(handle, HANDLE_DIR)) {
324 ret = closedir(handles[handle].dirp);
325 handles[handle].use = HANDLE_UNUSED;
326 } else {
327 errno = ENOENT;
328 }
329 return ret;
330}
331
332int
333get_handle(void)
334{
335 char *handle;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000336 int val = -1;
Damien Millere4340be2000-09-16 13:29:08 +1100337 u_int hlen;
Damien Miller7b28dc52000-09-05 13:34:53 +1100338 handle = get_string(&hlen);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000339 if (hlen < 256)
340 val = handle_from_string(handle, hlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100341 xfree(handle);
342 return val;
343}
344
345/* send replies */
346
347void
348send_msg(Buffer *m)
349{
350 int mlen = buffer_len(m);
351 buffer_put_int(&oqueue, mlen);
352 buffer_append(&oqueue, buffer_ptr(m), mlen);
353 buffer_consume(m, mlen);
354}
355
356void
357send_status(u_int32_t id, u_int32_t error)
358{
359 Buffer msg;
360 TRACE("sent status id %d error %d", id, error);
361 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000362 buffer_put_char(&msg, SSH2_FXP_STATUS);
Damien Miller7b28dc52000-09-05 13:34:53 +1100363 buffer_put_int(&msg, id);
364 buffer_put_int(&msg, error);
365 send_msg(&msg);
366 buffer_free(&msg);
367}
368void
369send_data_or_handle(char type, u_int32_t id, char *data, int dlen)
370{
371 Buffer msg;
372 buffer_init(&msg);
373 buffer_put_char(&msg, type);
374 buffer_put_int(&msg, id);
375 buffer_put_string(&msg, data, dlen);
376 send_msg(&msg);
377 buffer_free(&msg);
378}
379
380void
381send_data(u_int32_t id, char *data, int dlen)
382{
383 TRACE("sent data id %d len %d", id, dlen);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000384 send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100385}
386
387void
388send_handle(u_int32_t id, int handle)
389{
390 char *string;
391 int hlen;
392 handle_to_string(handle, &string, &hlen);
393 TRACE("sent handle id %d handle %d", id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000394 send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
Damien Miller7b28dc52000-09-05 13:34:53 +1100395 xfree(string);
396}
397
398void
399send_names(u_int32_t id, int count, Stat *stats)
400{
401 Buffer msg;
402 int i;
403 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000404 buffer_put_char(&msg, SSH2_FXP_NAME);
Damien Miller7b28dc52000-09-05 13:34:53 +1100405 buffer_put_int(&msg, id);
406 buffer_put_int(&msg, count);
407 TRACE("sent names id %d count %d", id, count);
408 for (i = 0; i < count; i++) {
409 buffer_put_cstring(&msg, stats[i].name);
410 buffer_put_cstring(&msg, stats[i].long_name);
411 encode_attrib(&msg, &stats[i].attrib);
412 }
413 send_msg(&msg);
414 buffer_free(&msg);
415}
416
417void
418send_attrib(u_int32_t id, Attrib *a)
419{
420 Buffer msg;
421 TRACE("sent attrib id %d have 0x%x", id, a->flags);
422 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000423 buffer_put_char(&msg, SSH2_FXP_ATTRS);
Damien Miller7b28dc52000-09-05 13:34:53 +1100424 buffer_put_int(&msg, id);
425 encode_attrib(&msg, a);
426 send_msg(&msg);
427 buffer_free(&msg);
428}
429
430/* parse incoming */
431
432void
433process_init(void)
434{
435 Buffer msg;
436 int version = buffer_get_int(&iqueue);
437
438 TRACE("client version %d", version);
439 buffer_init(&msg);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000440 buffer_put_char(&msg, SSH2_FXP_VERSION);
441 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
Damien Miller7b28dc52000-09-05 13:34:53 +1100442 send_msg(&msg);
443 buffer_free(&msg);
444}
445
446void
447process_open(void)
448{
449 u_int32_t id, pflags;
450 Attrib *a;
451 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000452 int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100453
454 id = get_int();
455 name = get_string(NULL);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000456 pflags = get_int(); /* portable flags */
Damien Miller7b28dc52000-09-05 13:34:53 +1100457 a = get_attrib();
458 flags = flags_from_portable(pflags);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000459 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
Damien Miller7b28dc52000-09-05 13:34:53 +1100460 TRACE("open id %d name %s flags %d mode 0%o", id, name, pflags, mode);
461 fd = open(name, flags, mode);
462 if (fd < 0) {
463 status = errno_to_portable(errno);
464 } else {
465 handle = handle_new(HANDLE_FILE, xstrdup(name), fd, NULL);
466 if (handle < 0) {
467 close(fd);
468 } else {
469 send_handle(id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000470 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100471 }
472 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000473 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100474 send_status(id, status);
475 xfree(name);
476}
477
478void
479process_close(void)
480{
481 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000482 int handle, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100483
484 id = get_int();
485 handle = get_handle();
486 TRACE("close id %d handle %d", id, handle);
487 ret = handle_close(handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000488 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100489 send_status(id, status);
490}
491
492void
493process_read(void)
494{
495 char buf[64*1024];
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000496 u_int32_t id, len;
497 int handle, fd, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100498 u_int64_t off;
499
500 id = get_int();
501 handle = get_handle();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000502 off = get_int64();
Damien Miller7b28dc52000-09-05 13:34:53 +1100503 len = get_int();
504
Damien Millerf056e232000-09-23 14:58:32 +1100505 TRACE("read id %d handle %d off %lld len %d", id, handle, off, len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100506 if (len > sizeof buf) {
507 len = sizeof buf;
508 log("read change len %d", len);
509 }
510 fd = handle_to_fd(handle);
511 if (fd >= 0) {
512 if (lseek(fd, off, SEEK_SET) < 0) {
513 error("process_read: seek failed");
514 status = errno_to_portable(errno);
515 } else {
516 ret = read(fd, buf, len);
517 if (ret < 0) {
518 status = errno_to_portable(errno);
519 } else if (ret == 0) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000520 status = SSH2_FX_EOF;
Damien Miller7b28dc52000-09-05 13:34:53 +1100521 } else {
522 send_data(id, buf, ret);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000523 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100524 }
525 }
526 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000527 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100528 send_status(id, status);
529}
530
531void
532process_write(void)
533{
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000534 u_int32_t id;
Damien Miller7b28dc52000-09-05 13:34:53 +1100535 u_int64_t off;
Damien Millere4340be2000-09-16 13:29:08 +1100536 u_int len;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000537 int handle, fd, ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100538 char *data;
539
540 id = get_int();
541 handle = get_handle();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000542 off = get_int64();
Damien Miller7b28dc52000-09-05 13:34:53 +1100543 data = get_string(&len);
544
Damien Millerf056e232000-09-23 14:58:32 +1100545 TRACE("write id %d handle %d off %lld len %d", id, handle, off, len);
Damien Miller7b28dc52000-09-05 13:34:53 +1100546 fd = handle_to_fd(handle);
547 if (fd >= 0) {
548 if (lseek(fd, off, SEEK_SET) < 0) {
549 status = errno_to_portable(errno);
550 error("process_write: seek failed");
551 } else {
552/* XXX ATOMICIO ? */
553 ret = write(fd, data, len);
554 if (ret == -1) {
555 error("process_write: write failed");
556 status = errno_to_portable(errno);
557 } else if (ret == len) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000558 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100559 } else {
560 log("nothing at all written");
561 }
562 }
563 }
564 send_status(id, status);
565 xfree(data);
566}
567
568void
569process_do_stat(int do_lstat)
570{
571 Attrib *a;
572 struct stat st;
573 u_int32_t id;
574 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000575 int ret, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100576
577 id = get_int();
578 name = get_string(NULL);
579 TRACE("%sstat id %d name %s", do_lstat ? "l" : "", id, name);
580 ret = do_lstat ? lstat(name, &st) : stat(name, &st);
581 if (ret < 0) {
582 status = errno_to_portable(errno);
583 } else {
584 a = stat_to_attrib(&st);
585 send_attrib(id, a);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000586 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100587 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000588 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100589 send_status(id, status);
590 xfree(name);
591}
592
593void
594process_stat(void)
595{
596 process_do_stat(0);
597}
598
599void
600process_lstat(void)
601{
602 process_do_stat(1);
603}
604
605void
606process_fstat(void)
607{
608 Attrib *a;
609 struct stat st;
610 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000611 int fd, ret, handle, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100612
613 id = get_int();
614 handle = get_handle();
615 TRACE("fstat id %d handle %d", id, handle);
616 fd = handle_to_fd(handle);
617 if (fd >= 0) {
618 ret = fstat(fd, &st);
619 if (ret < 0) {
620 status = errno_to_portable(errno);
621 } else {
622 a = stat_to_attrib(&st);
623 send_attrib(id, a);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000624 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100625 }
626 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000627 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100628 send_status(id, status);
629}
630
631struct timeval *
632attrib_to_tv(Attrib *a)
633{
634 static struct timeval tv[2];
635 tv[0].tv_sec = a->atime;
636 tv[0].tv_usec = 0;
637 tv[1].tv_sec = a->mtime;
638 tv[1].tv_usec = 0;
639 return tv;
640}
641
642void
643process_setstat(void)
644{
645 Attrib *a;
646 u_int32_t id;
647 char *name;
648 int ret;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000649 int status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100650
651 id = get_int();
652 name = get_string(NULL);
653 a = get_attrib();
654 TRACE("setstat id %d name %s", id, name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000655 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100656 ret = chmod(name, a->perm & 0777);
657 if (ret == -1)
658 status = errno_to_portable(errno);
659 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000660 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100661 ret = utimes(name, attrib_to_tv(a));
662 if (ret == -1)
663 status = errno_to_portable(errno);
664 }
665 send_status(id, status);
666 xfree(name);
667}
668
669void
670process_fsetstat(void)
671{
672 Attrib *a;
673 u_int32_t id;
674 int handle, fd, ret;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000675 int status = SSH2_FX_OK;
Damien Millere4340be2000-09-16 13:29:08 +1100676 char *name;
677
Damien Miller7b28dc52000-09-05 13:34:53 +1100678 id = get_int();
679 handle = get_handle();
680 a = get_attrib();
681 TRACE("fsetstat id %d handle %d", id, handle);
682 fd = handle_to_fd(handle);
683 name = handle_to_name(handle);
684 if ((fd < 0) || (name == NULL)) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000685 status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100686 } else {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000687 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000688#ifdef HAVE_FCHMOD
Damien Miller7b28dc52000-09-05 13:34:53 +1100689 ret = fchmod(fd, a->perm & 0777);
Ben Lindstrom200e3c92001-01-15 01:56:46 +0000690#else
691 ret = chmod(name, a->perm & 077);
692#endif
Damien Miller7b28dc52000-09-05 13:34:53 +1100693 if (ret == -1)
694 status = errno_to_portable(errno);
695 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000696 if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
Damien Miller7b28dc52000-09-05 13:34:53 +1100697#ifdef HAVE_FUTIMES
698 ret = futimes(fd, attrib_to_tv(a));
699#else
700 ret = utimes(name, attrib_to_tv(a));
701#endif
702 if (ret == -1)
703 status = errno_to_portable(errno);
704 }
705 }
706 send_status(id, status);
707}
708
709void
710process_opendir(void)
711{
712 DIR *dirp = NULL;
713 char *path;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000714 int handle, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100715 u_int32_t id;
716
717 id = get_int();
718 path = get_string(NULL);
719 TRACE("opendir id %d path %s", id, path);
720 dirp = opendir(path);
721 if (dirp == NULL) {
722 status = errno_to_portable(errno);
723 } else {
724 handle = handle_new(HANDLE_DIR, xstrdup(path), 0, dirp);
725 if (handle < 0) {
726 closedir(dirp);
727 } else {
728 send_handle(id, handle);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000729 status = SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100730 }
731
732 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000733 if (status != SSH2_FX_OK)
Damien Miller7b28dc52000-09-05 13:34:53 +1100734 send_status(id, status);
735 xfree(path);
736}
737
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000738/*
739 * XXX, draft-ietf-secsh-filexfer-00.txt says:
740 * The recommended format for the longname field is as follows:
741 * -rwxr-xr-x 1 mjos staff 348911 Mar 25 14:29 t-filexfer
742 * 1234567890 123 12345678 12345678 12345678 123456789012
743 */
Damien Miller7b28dc52000-09-05 13:34:53 +1100744char *
745ls_file(char *name, struct stat *st)
746{
747 char buf[1024];
Damien Millerf056e232000-09-23 14:58:32 +1100748 snprintf(buf, sizeof buf, "0%o %d %d %lld %d %s",
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000749 st->st_mode, st->st_uid, st->st_gid, (long long)st->st_size,
750 (int)st->st_mtime, name);
Damien Miller7b28dc52000-09-05 13:34:53 +1100751 return xstrdup(buf);
752}
753
754void
755process_readdir(void)
756{
757 DIR *dirp;
758 struct dirent *dp;
759 char *path;
760 int handle;
761 u_int32_t id;
762
763 id = get_int();
764 handle = get_handle();
765 TRACE("readdir id %d handle %d", id, handle);
766 dirp = handle_to_dir(handle);
767 path = handle_to_name(handle);
768 if (dirp == NULL || path == NULL) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000769 send_status(id, SSH2_FX_FAILURE);
Damien Miller7b28dc52000-09-05 13:34:53 +1100770 } else {
771 Attrib *a;
772 struct stat st;
773 char pathname[1024];
774 Stat *stats;
775 int nstats = 10, count = 0, i;
776 stats = xmalloc(nstats * sizeof(Stat));
777 while ((dp = readdir(dirp)) != NULL) {
778 if (count >= nstats) {
779 nstats *= 2;
780 stats = xrealloc(stats, nstats * sizeof(Stat));
781 }
782/* XXX OVERFLOW ? */
783 snprintf(pathname, sizeof pathname,
784 "%s/%s", path, dp->d_name);
785 if (lstat(pathname, &st) < 0)
786 continue;
787 a = stat_to_attrib(&st);
788 stats[count].attrib = *a;
789 stats[count].name = xstrdup(dp->d_name);
790 stats[count].long_name = ls_file(dp->d_name, &st);
791 count++;
792 /* send up to 100 entries in one message */
793 if (count == 100)
794 break;
795 }
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000796 if (count > 0) {
797 send_names(id, count, stats);
798 for(i = 0; i < count; i++) {
799 xfree(stats[i].name);
800 xfree(stats[i].long_name);
801 }
802 } else {
803 send_status(id, SSH2_FX_EOF);
Damien Miller7b28dc52000-09-05 13:34:53 +1100804 }
805 xfree(stats);
806 }
807}
808
809void
810process_remove(void)
811{
812 char *name;
813 u_int32_t id;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000814 int status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100815 int ret;
816
817 id = get_int();
818 name = get_string(NULL);
819 TRACE("remove id %d name %s", id, name);
Kevin Stevesa074feb2000-12-21 22:33:45 +0000820 ret = unlink(name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000821 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100822 send_status(id, status);
823 xfree(name);
824}
825
826void
827process_mkdir(void)
828{
829 Attrib *a;
830 u_int32_t id;
831 char *name;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000832 int ret, mode, status = SSH2_FX_FAILURE;
Damien Miller7b28dc52000-09-05 13:34:53 +1100833
834 id = get_int();
835 name = get_string(NULL);
836 a = get_attrib();
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000837 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
838 a->perm & 0777 : 0777;
Damien Miller7b28dc52000-09-05 13:34:53 +1100839 TRACE("mkdir id %d name %s mode 0%o", id, name, mode);
840 ret = mkdir(name, mode);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000841 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100842 send_status(id, status);
843 xfree(name);
844}
845
846void
847process_rmdir(void)
848{
849 u_int32_t id;
850 char *name;
851 int ret, status;
852
853 id = get_int();
854 name = get_string(NULL);
855 TRACE("rmdir id %d name %s", id, name);
856 ret = rmdir(name);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000857 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100858 send_status(id, status);
859 xfree(name);
860}
861
862void
863process_realpath(void)
864{
865 char resolvedname[MAXPATHLEN];
866 u_int32_t id;
867 char *path;
868
869 id = get_int();
870 path = get_string(NULL);
Ben Lindstromfa1b3d02000-12-10 01:55:37 +0000871 if (path[0] == '\0') {
872 xfree(path);
873 path = xstrdup(".");
874 }
Damien Miller7b28dc52000-09-05 13:34:53 +1100875 TRACE("realpath id %d path %s", id, path);
876 if (realpath(path, resolvedname) == NULL) {
877 send_status(id, errno_to_portable(errno));
878 } else {
879 Stat s;
880 attrib_clear(&s.attrib);
881 s.name = s.long_name = resolvedname;
882 send_names(id, 1, &s);
883 }
884 xfree(path);
885}
886
887void
888process_rename(void)
889{
890 u_int32_t id;
891 char *oldpath, *newpath;
892 int ret, status;
893
894 id = get_int();
895 oldpath = get_string(NULL);
896 newpath = get_string(NULL);
897 TRACE("rename id %d old %s new %s", id, oldpath, newpath);
898 ret = rename(oldpath, newpath);
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000899 status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
Damien Miller7b28dc52000-09-05 13:34:53 +1100900 send_status(id, status);
901 xfree(oldpath);
902 xfree(newpath);
903}
904
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000905void
906process_extended(void)
907{
908 u_int32_t id;
909 char *request;
910
911 id = get_int();
912 request = get_string(NULL);
913 send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
914 xfree(request);
915}
Damien Miller7b28dc52000-09-05 13:34:53 +1100916
917/* stolen from ssh-agent */
918
919void
920process(void)
921{
Ben Lindstrom46c16222000-12-22 01:43:59 +0000922 u_int msg_len;
923 u_int type;
924 u_char *cp;
Damien Miller7b28dc52000-09-05 13:34:53 +1100925
926 if (buffer_len(&iqueue) < 5)
927 return; /* Incomplete message. */
Ben Lindstrom46c16222000-12-22 01:43:59 +0000928 cp = (u_char *) buffer_ptr(&iqueue);
Damien Miller7b28dc52000-09-05 13:34:53 +1100929 msg_len = GET_32BIT(cp);
930 if (msg_len > 256 * 1024) {
931 error("bad message ");
932 exit(11);
933 }
934 if (buffer_len(&iqueue) < msg_len + 4)
935 return;
936 buffer_consume(&iqueue, 4);
937 type = buffer_get_char(&iqueue);
938 switch (type) {
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000939 case SSH2_FXP_INIT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100940 process_init();
941 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000942 case SSH2_FXP_OPEN:
Damien Miller7b28dc52000-09-05 13:34:53 +1100943 process_open();
944 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000945 case SSH2_FXP_CLOSE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100946 process_close();
947 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000948 case SSH2_FXP_READ:
Damien Miller7b28dc52000-09-05 13:34:53 +1100949 process_read();
950 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000951 case SSH2_FXP_WRITE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100952 process_write();
953 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000954 case SSH2_FXP_LSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100955 process_lstat();
956 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000957 case SSH2_FXP_FSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100958 process_fstat();
959 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000960 case SSH2_FXP_SETSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100961 process_setstat();
962 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000963 case SSH2_FXP_FSETSTAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100964 process_fsetstat();
965 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000966 case SSH2_FXP_OPENDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100967 process_opendir();
968 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000969 case SSH2_FXP_READDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100970 process_readdir();
971 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000972 case SSH2_FXP_REMOVE:
Damien Miller7b28dc52000-09-05 13:34:53 +1100973 process_remove();
974 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000975 case SSH2_FXP_MKDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100976 process_mkdir();
977 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000978 case SSH2_FXP_RMDIR:
Damien Miller7b28dc52000-09-05 13:34:53 +1100979 process_rmdir();
980 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000981 case SSH2_FXP_REALPATH:
Damien Miller7b28dc52000-09-05 13:34:53 +1100982 process_realpath();
983 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000984 case SSH2_FXP_STAT:
Damien Miller7b28dc52000-09-05 13:34:53 +1100985 process_stat();
986 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000987 case SSH2_FXP_RENAME:
Damien Miller7b28dc52000-09-05 13:34:53 +1100988 process_rename();
989 break;
Ben Lindstrom2f959b42001-01-11 06:20:23 +0000990 case SSH2_FXP_EXTENDED:
991 process_extended();
992 break;
Damien Miller7b28dc52000-09-05 13:34:53 +1100993 default:
994 error("Unknown message %d", type);
995 break;
996 }
997}
998
999int
1000main(int ac, char **av)
1001{
1002 fd_set rset, wset;
1003 int in, out, max;
Damien Millere4340be2000-09-16 13:29:08 +11001004 ssize_t len, olen;
Damien Miller7b28dc52000-09-05 13:34:53 +11001005
Ben Lindstrom49a79c02000-11-17 03:47:20 +00001006 __progname = get_progname(av[0]);
Damien Miller7b28dc52000-09-05 13:34:53 +11001007 handle_init();
1008
Ben Lindstrom2f959b42001-01-11 06:20:23 +00001009 log_init("sftp-server", SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 0);
1010
Damien Miller7b28dc52000-09-05 13:34:53 +11001011 in = dup(STDIN_FILENO);
1012 out = dup(STDOUT_FILENO);
1013
1014 max = 0;
1015 if (in > max)
1016 max = in;
1017 if (out > max)
1018 max = out;
1019
1020 buffer_init(&iqueue);
1021 buffer_init(&oqueue);
1022
1023 for (;;) {
1024 FD_ZERO(&rset);
1025 FD_ZERO(&wset);
1026
1027 FD_SET(in, &rset);
1028 olen = buffer_len(&oqueue);
1029 if (olen > 0)
1030 FD_SET(out, &wset);
1031
1032 if (select(max+1, &rset, &wset, NULL, NULL) < 0) {
1033 if (errno == EINTR)
1034 continue;
1035 exit(2);
1036 }
1037
1038 /* copy stdin to iqueue */
1039 if (FD_ISSET(in, &rset)) {
1040 char buf[4*4096];
1041 len = read(in, buf, sizeof buf);
1042 if (len == 0) {
1043 debug("read eof");
1044 exit(0);
1045 } else if (len < 0) {
1046 error("read error");
1047 exit(1);
1048 } else {
1049 buffer_append(&iqueue, buf, len);
1050 }
1051 }
1052 /* send oqueue to stdout */
1053 if (FD_ISSET(out, &wset)) {
1054 len = write(out, buffer_ptr(&oqueue), olen);
1055 if (len < 0) {
1056 error("write error");
1057 exit(1);
1058 } else {
1059 buffer_consume(&oqueue, len);
1060 }
1061 }
1062 /* process requests from client */
1063 process();
1064 }
1065}