Branch data Line data Source code
1 : : /* $OpenBSD: sftp-server.c,v 1.103 2014/01/17 06:23:24 dtucker Exp $ */
2 : : /*
3 : : * Copyright (c) 2000-2004 Markus Friedl. All rights reserved.
4 : : *
5 : : * Permission to use, copy, modify, and distribute this software for any
6 : : * purpose with or without fee is hereby granted, provided that the above
7 : : * copyright notice and this permission notice appear in all copies.
8 : : *
9 : : * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 : : * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 : : * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 : : * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 : : * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 : : * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 : : * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 : : */
17 : :
18 : : #include "includes.h"
19 : :
20 : : #include <sys/types.h>
21 : : #include <sys/param.h>
22 : : #include <sys/stat.h>
23 : : #ifdef HAVE_SYS_TIME_H
24 : : # include <sys/time.h>
25 : : #endif
26 : : #ifdef HAVE_SYS_MOUNT_H
27 : : #include <sys/mount.h>
28 : : #endif
29 : : #ifdef HAVE_SYS_STATVFS_H
30 : : #include <sys/statvfs.h>
31 : : #endif
32 : :
33 : : #include <dirent.h>
34 : : #include <errno.h>
35 : : #include <fcntl.h>
36 : : #include <pwd.h>
37 : : #include <stdlib.h>
38 : : #include <stdio.h>
39 : : #include <string.h>
40 : : #include <pwd.h>
41 : : #include <time.h>
42 : : #include <unistd.h>
43 : : #include <stdarg.h>
44 : :
45 : : #include "xmalloc.h"
46 : : #include "buffer.h"
47 : : #include "log.h"
48 : : #include "misc.h"
49 : : #include "match.h"
50 : : #include "uidswap.h"
51 : :
52 : : #include "sftp.h"
53 : : #include "sftp-common.h"
54 : :
55 : : /* helper */
56 : : #define get_int64() buffer_get_int64(&iqueue);
57 : : #define get_int() buffer_get_int(&iqueue);
58 : : #define get_string(lenp) buffer_get_string(&iqueue, lenp);
59 : :
60 : : /* Our verbosity */
61 : : static LogLevel log_level = SYSLOG_LEVEL_ERROR;
62 : :
63 : : /* Our client */
64 : : static struct passwd *pw = NULL;
65 : : static char *client_addr = NULL;
66 : :
67 : : /* input and output queue */
68 : : static Buffer iqueue;
69 : : static Buffer oqueue;
70 : :
71 : : /* Version of client */
72 : : static u_int version;
73 : :
74 : : /* SSH2_FXP_INIT received */
75 : : static int init_done;
76 : :
77 : : /* Disable writes */
78 : : static int readonly;
79 : :
80 : : /* Requests that are allowed/denied */
81 : : static char *request_whitelist, *request_blacklist;
82 : :
83 : : /* portable attributes, etc. */
84 : : typedef struct Stat Stat;
85 : :
86 : : struct Stat {
87 : : char *name;
88 : : char *long_name;
89 : : Attrib attrib;
90 : : };
91 : :
92 : : /* Packet handlers */
93 : : static void process_open(u_int32_t id);
94 : : static void process_close(u_int32_t id);
95 : : static void process_read(u_int32_t id);
96 : : static void process_write(u_int32_t id);
97 : : static void process_stat(u_int32_t id);
98 : : static void process_lstat(u_int32_t id);
99 : : static void process_fstat(u_int32_t id);
100 : : static void process_setstat(u_int32_t id);
101 : : static void process_fsetstat(u_int32_t id);
102 : : static void process_opendir(u_int32_t id);
103 : : static void process_readdir(u_int32_t id);
104 : : static void process_remove(u_int32_t id);
105 : : static void process_mkdir(u_int32_t id);
106 : : static void process_rmdir(u_int32_t id);
107 : : static void process_realpath(u_int32_t id);
108 : : static void process_rename(u_int32_t id);
109 : : static void process_readlink(u_int32_t id);
110 : : static void process_symlink(u_int32_t id);
111 : : static void process_extended_posix_rename(u_int32_t id);
112 : : static void process_extended_statvfs(u_int32_t id);
113 : : static void process_extended_fstatvfs(u_int32_t id);
114 : : static void process_extended_hardlink(u_int32_t id);
115 : : static void process_extended_fsync(u_int32_t id);
116 : : static void process_extended(u_int32_t id);
117 : :
118 : : struct sftp_handler {
119 : : const char *name; /* user-visible name for fine-grained perms */
120 : : const char *ext_name; /* extended request name */
121 : : u_int type; /* packet type, for non extended packets */
122 : : void (*handler)(u_int32_t);
123 : : int does_write; /* if nonzero, banned for readonly mode */
124 : : };
125 : :
126 : : struct sftp_handler handlers[] = {
127 : : /* NB. SSH2_FXP_OPEN does the readonly check in the handler itself */
128 : : { "open", NULL, SSH2_FXP_OPEN, process_open, 0 },
129 : : { "close", NULL, SSH2_FXP_CLOSE, process_close, 0 },
130 : : { "read", NULL, SSH2_FXP_READ, process_read, 0 },
131 : : { "write", NULL, SSH2_FXP_WRITE, process_write, 1 },
132 : : { "lstat", NULL, SSH2_FXP_LSTAT, process_lstat, 0 },
133 : : { "fstat", NULL, SSH2_FXP_FSTAT, process_fstat, 0 },
134 : : { "setstat", NULL, SSH2_FXP_SETSTAT, process_setstat, 1 },
135 : : { "fsetstat", NULL, SSH2_FXP_FSETSTAT, process_fsetstat, 1 },
136 : : { "opendir", NULL, SSH2_FXP_OPENDIR, process_opendir, 0 },
137 : : { "readdir", NULL, SSH2_FXP_READDIR, process_readdir, 0 },
138 : : { "remove", NULL, SSH2_FXP_REMOVE, process_remove, 1 },
139 : : { "mkdir", NULL, SSH2_FXP_MKDIR, process_mkdir, 1 },
140 : : { "rmdir", NULL, SSH2_FXP_RMDIR, process_rmdir, 1 },
141 : : { "realpath", NULL, SSH2_FXP_REALPATH, process_realpath, 0 },
142 : : { "stat", NULL, SSH2_FXP_STAT, process_stat, 0 },
143 : : { "rename", NULL, SSH2_FXP_RENAME, process_rename, 1 },
144 : : { "readlink", NULL, SSH2_FXP_READLINK, process_readlink, 0 },
145 : : { "symlink", NULL, SSH2_FXP_SYMLINK, process_symlink, 1 },
146 : : { NULL, NULL, 0, NULL, 0 }
147 : : };
148 : :
149 : : /* SSH2_FXP_EXTENDED submessages */
150 : : struct sftp_handler extended_handlers[] = {
151 : : { "posix-rename", "posix-rename@openssh.com", 0,
152 : : process_extended_posix_rename, 1 },
153 : : { "statvfs", "statvfs@openssh.com", 0, process_extended_statvfs, 0 },
154 : : { "fstatvfs", "fstatvfs@openssh.com", 0, process_extended_fstatvfs, 0 },
155 : : { "hardlink", "hardlink@openssh.com", 0, process_extended_hardlink, 1 },
156 : : { "fsync", "fsync@openssh.com", 0, process_extended_fsync, 1 },
157 : : { NULL, NULL, 0, NULL, 0 }
158 : : };
159 : :
160 : : static int
161 : 0 : request_permitted(struct sftp_handler *h)
162 : : {
163 : : char *result;
164 : :
165 [ # # ][ # # ]: 0 : if (readonly && h->does_write) {
166 : 0 : verbose("Refusing %s request in read-only mode", h->name);
167 : : return 0;
168 : : }
169 [ # # ][ # # ]: 0 : if (request_blacklist != NULL &&
170 : 0 : ((result = match_list(h->name, request_blacklist, NULL))) != NULL) {
171 : 0 : free(result);
172 : 0 : verbose("Refusing blacklisted %s request", h->name);
173 : : return 0;
174 : : }
175 [ # # ][ # # ]: 0 : if (request_whitelist != NULL &&
176 : 0 : ((result = match_list(h->name, request_whitelist, NULL))) != NULL) {
177 : 0 : free(result);
178 : 0 : debug2("Permitting whitelisted %s request", h->name);
179 : : return 1;
180 : : }
181 [ # # ]: 0 : if (request_whitelist != NULL) {
182 : 0 : verbose("Refusing non-whitelisted %s request", h->name);
183 : : return 0;
184 : : }
185 : : return 1;
186 : : }
187 : :
188 : : static int
189 : : errno_to_portable(int unixerrno)
190 : : {
191 [ # # ][ # # ]: 0 : int ret = 0;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
192 : :
193 : : switch (unixerrno) {
194 : : case 0:
195 : : ret = SSH2_FX_OK;
196 : : break;
197 : : case ENOENT:
198 : : case ENOTDIR:
199 : : case EBADF:
200 : : case ELOOP:
201 : : ret = SSH2_FX_NO_SUCH_FILE;
202 : : break;
203 : : case EPERM:
204 : : case EACCES:
205 : : case EFAULT:
206 : : ret = SSH2_FX_PERMISSION_DENIED;
207 : : break;
208 : : case ENAMETOOLONG:
209 : : case EINVAL:
210 : : ret = SSH2_FX_BAD_MESSAGE;
211 : : break;
212 : : case ENOSYS:
213 : : ret = SSH2_FX_OP_UNSUPPORTED;
214 : : break;
215 : : default:
216 : : ret = SSH2_FX_FAILURE;
217 : : break;
218 : : }
219 : : return ret;
220 : : }
221 : :
222 : : static int
223 : 0 : flags_from_portable(int pflags)
224 : : {
225 : 0 : int flags = 0;
226 : :
227 [ # # ]: 0 : if ((pflags & SSH2_FXF_READ) &&
228 : : (pflags & SSH2_FXF_WRITE)) {
229 : : flags = O_RDWR;
230 [ # # ]: 0 : } else if (pflags & SSH2_FXF_READ) {
231 : : flags = O_RDONLY;
232 [ # # ]: 0 : } else if (pflags & SSH2_FXF_WRITE) {
233 : 0 : flags = O_WRONLY;
234 : : }
235 [ # # ]: 0 : if (pflags & SSH2_FXF_APPEND)
236 : 0 : flags |= O_APPEND;
237 [ # # ]: 0 : if (pflags & SSH2_FXF_CREAT)
238 : 0 : flags |= O_CREAT;
239 [ # # ]: 0 : if (pflags & SSH2_FXF_TRUNC)
240 : 0 : flags |= O_TRUNC;
241 [ # # ]: 0 : if (pflags & SSH2_FXF_EXCL)
242 : 0 : flags |= O_EXCL;
243 : 0 : return flags;
244 : : }
245 : :
246 : : static const char *
247 : 0 : string_from_portable(int pflags)
248 : : {
249 : : static char ret[128];
250 : :
251 : 0 : *ret = '\0';
252 : :
253 : : #define PAPPEND(str) { \
254 : : if (*ret != '\0') \
255 : : strlcat(ret, ",", sizeof(ret)); \
256 : : strlcat(ret, str, sizeof(ret)); \
257 : : }
258 : :
259 [ # # ]: 0 : if (pflags & SSH2_FXF_READ)
260 : 0 : PAPPEND("READ")
261 [ # # ]: 0 : if (pflags & SSH2_FXF_WRITE)
262 [ # # ]: 0 : PAPPEND("WRITE")
263 [ # # ]: 0 : if (pflags & SSH2_FXF_APPEND)
264 [ # # ]: 0 : PAPPEND("APPEND")
265 [ # # ]: 0 : if (pflags & SSH2_FXF_CREAT)
266 [ # # ]: 0 : PAPPEND("CREATE")
267 [ # # ]: 0 : if (pflags & SSH2_FXF_TRUNC)
268 [ # # ]: 0 : PAPPEND("TRUNCATE")
269 [ # # ]: 0 : if (pflags & SSH2_FXF_EXCL)
270 [ # # ]: 0 : PAPPEND("EXCL")
271 : :
272 : 0 : return ret;
273 : : }
274 : :
275 : : static Attrib *
276 : : get_attrib(void)
277 : : {
278 : 0 : return decode_attrib(&iqueue);
279 : : }
280 : :
281 : : /* handle handles */
282 : :
283 : : typedef struct Handle Handle;
284 : : struct Handle {
285 : : int use;
286 : : DIR *dirp;
287 : : int fd;
288 : : int flags;
289 : : char *name;
290 : : u_int64_t bytes_read, bytes_write;
291 : : int next_unused;
292 : : };
293 : :
294 : : enum {
295 : : HANDLE_UNUSED,
296 : : HANDLE_DIR,
297 : : HANDLE_FILE
298 : : };
299 : :
300 : : Handle *handles = NULL;
301 : : u_int num_handles = 0;
302 : : int first_unused_handle = -1;
303 : :
304 : : static void handle_unused(int i)
305 : : {
306 : 0 : handles[i].use = HANDLE_UNUSED;
307 : 0 : handles[i].next_unused = first_unused_handle;
308 : 0 : first_unused_handle = i;
309 : : }
310 : :
311 : : static int
312 : 0 : handle_new(int use, const char *name, int fd, int flags, DIR *dirp)
313 : : {
314 : : int i;
315 : :
316 [ # # ]: 0 : if (first_unused_handle == -1) {
317 [ # # ]: 0 : if (num_handles + 1 <= num_handles)
318 : : return -1;
319 : 0 : num_handles++;
320 : 0 : handles = xrealloc(handles, num_handles, sizeof(Handle));
321 : 0 : handle_unused(num_handles - 1);
322 : : }
323 : :
324 : 0 : i = first_unused_handle;
325 : 0 : first_unused_handle = handles[i].next_unused;
326 : :
327 : 0 : handles[i].use = use;
328 : 0 : handles[i].dirp = dirp;
329 : 0 : handles[i].fd = fd;
330 : 0 : handles[i].flags = flags;
331 : 0 : handles[i].name = xstrdup(name);
332 : 0 : handles[i].bytes_read = handles[i].bytes_write = 0;
333 : :
334 : 0 : return i;
335 : : }
336 : :
337 : : static int
338 : : handle_is_ok(int i, int type)
339 : : {
340 [ # # ][ # # ]: 0 : return i >= 0 && (u_int)i < num_handles && handles[i].use == type;
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ][ # # ]
[ # # ]
341 : : }
342 : :
343 : : static int
344 : 0 : handle_to_string(int handle, char **stringp, int *hlenp)
345 : : {
346 [ # # ]: 0 : if (stringp == NULL || hlenp == NULL)
347 : : return -1;
348 : 0 : *stringp = xmalloc(sizeof(int32_t));
349 : 0 : put_u32(*stringp, handle);
350 : 0 : *hlenp = sizeof(int32_t);
351 : 0 : return 0;
352 : : }
353 : :
354 : : static int
355 : 0 : handle_from_string(const char *handle, u_int hlen)
356 : : {
357 : : int val;
358 : :
359 [ # # ]: 0 : if (hlen != sizeof(int32_t))
360 : : return -1;
361 : 0 : val = get_u32(handle);
362 [ # # ][ # # ]: 0 : if (handle_is_ok(val, HANDLE_FILE) ||
363 : : handle_is_ok(val, HANDLE_DIR))
364 : 0 : return val;
365 : : return -1;
366 : : }
367 : :
368 : : static char *
369 : 0 : handle_to_name(int handle)
370 : : {
371 [ # # ][ # # ]: 0 : if (handle_is_ok(handle, HANDLE_DIR)||
372 : : handle_is_ok(handle, HANDLE_FILE))
373 : 0 : return handles[handle].name;
374 : : return NULL;
375 : : }
376 : :
377 : : static DIR *
378 : 0 : handle_to_dir(int handle)
379 : : {
380 [ # # ]: 0 : if (handle_is_ok(handle, HANDLE_DIR))
381 : 0 : return handles[handle].dirp;
382 : : return NULL;
383 : : }
384 : :
385 : : static int
386 : 0 : handle_to_fd(int handle)
387 : : {
388 [ # # ]: 0 : if (handle_is_ok(handle, HANDLE_FILE))
389 : 0 : return handles[handle].fd;
390 : : return -1;
391 : : }
392 : :
393 : : static int
394 : 0 : handle_to_flags(int handle)
395 : : {
396 [ # # ]: 0 : if (handle_is_ok(handle, HANDLE_FILE))
397 : 0 : return handles[handle].flags;
398 : : return 0;
399 : : }
400 : :
401 : : static void
402 : 0 : handle_update_read(int handle, ssize_t bytes)
403 : : {
404 [ # # ][ # # ]: 0 : if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0)
405 : 0 : handles[handle].bytes_read += bytes;
406 : 0 : }
407 : :
408 : : static void
409 : 0 : handle_update_write(int handle, ssize_t bytes)
410 : : {
411 [ # # ][ # # ]: 0 : if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0)
412 : 0 : handles[handle].bytes_write += bytes;
413 : 0 : }
414 : :
415 : : static u_int64_t
416 : 0 : handle_bytes_read(int handle)
417 : : {
418 [ # # ]: 0 : if (handle_is_ok(handle, HANDLE_FILE))
419 : 0 : return (handles[handle].bytes_read);
420 : : return 0;
421 : : }
422 : :
423 : : static u_int64_t
424 : 0 : handle_bytes_write(int handle)
425 : : {
426 [ # # ]: 0 : if (handle_is_ok(handle, HANDLE_FILE))
427 : 0 : return (handles[handle].bytes_write);
428 : : return 0;
429 : : }
430 : :
431 : : static int
432 : 0 : handle_close(int handle)
433 : : {
434 : 0 : int ret = -1;
435 : :
436 [ # # ]: 0 : if (handle_is_ok(handle, HANDLE_FILE)) {
437 : 0 : ret = close(handles[handle].fd);
438 : 0 : free(handles[handle].name);
439 : : handle_unused(handle);
440 [ # # ]: 0 : } else if (handle_is_ok(handle, HANDLE_DIR)) {
441 : 0 : ret = closedir(handles[handle].dirp);
442 : 0 : free(handles[handle].name);
443 : : handle_unused(handle);
444 : : } else {
445 : 0 : errno = ENOENT;
446 : : }
447 : 0 : return ret;
448 : : }
449 : :
450 : : static void
451 : 0 : handle_log_close(int handle, char *emsg)
452 : : {
453 [ # # ]: 0 : if (handle_is_ok(handle, HANDLE_FILE)) {
454 [ # # ][ # # ]: 0 : logit("%s%sclose \"%s\" bytes read %llu written %llu",
455 : : emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ",
456 : : handle_to_name(handle),
457 : 0 : (unsigned long long)handle_bytes_read(handle),
458 : 0 : (unsigned long long)handle_bytes_write(handle));
459 : : } else {
460 [ # # ][ # # ]: 0 : logit("%s%sclosedir \"%s\"",
461 : : emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ",
462 : : handle_to_name(handle));
463 : : }
464 : 0 : }
465 : :
466 : : static void
467 : 0 : handle_log_exit(void)
468 : : {
469 : : u_int i;
470 : :
471 [ # # ]: 0 : for (i = 0; i < num_handles; i++)
472 [ # # ]: 0 : if (handles[i].use != HANDLE_UNUSED)
473 : 0 : handle_log_close(i, "forced");
474 : 0 : }
475 : :
476 : : static int
477 : 0 : get_handle(void)
478 : : {
479 : : char *handle;
480 : 0 : int val = -1;
481 : : u_int hlen;
482 : :
483 : 0 : handle = get_string(&hlen);
484 [ # # ]: 0 : if (hlen < 256)
485 : 0 : val = handle_from_string(handle, hlen);
486 : 0 : free(handle);
487 : 0 : return val;
488 : : }
489 : :
490 : : /* send replies */
491 : :
492 : : static void
493 : 0 : send_msg(Buffer *m)
494 : : {
495 : 0 : int mlen = buffer_len(m);
496 : :
497 : 0 : buffer_put_int(&oqueue, mlen);
498 : 0 : buffer_append(&oqueue, buffer_ptr(m), mlen);
499 : 0 : buffer_consume(m, mlen);
500 : 0 : }
501 : :
502 : : static const char *
503 : : status_to_message(u_int32_t status)
504 : : {
505 : 0 : const char *status_messages[] = {
506 : : "Success", /* SSH_FX_OK */
507 : : "End of file", /* SSH_FX_EOF */
508 : : "No such file", /* SSH_FX_NO_SUCH_FILE */
509 : : "Permission denied", /* SSH_FX_PERMISSION_DENIED */
510 : : "Failure", /* SSH_FX_FAILURE */
511 : : "Bad message", /* SSH_FX_BAD_MESSAGE */
512 : : "No connection", /* SSH_FX_NO_CONNECTION */
513 : : "Connection lost", /* SSH_FX_CONNECTION_LOST */
514 : : "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */
515 : : "Unknown error" /* Others */
516 : : };
517 : 0 : return (status_messages[MIN(status,SSH2_FX_MAX)]);
518 : : }
519 : :
520 : : static void
521 : 0 : send_status(u_int32_t id, u_int32_t status)
522 : : {
523 : : Buffer msg;
524 : :
525 : 0 : debug3("request %u: sent status %u", id, status);
526 [ # # ][ # # ]: 0 : if (log_level > SYSLOG_LEVEL_VERBOSE ||
527 : : (status != SSH2_FX_OK && status != SSH2_FX_EOF))
528 : 0 : logit("sent status %s", status_to_message(status));
529 : 0 : buffer_init(&msg);
530 : 0 : buffer_put_char(&msg, SSH2_FXP_STATUS);
531 : 0 : buffer_put_int(&msg, id);
532 : 0 : buffer_put_int(&msg, status);
533 [ # # ]: 0 : if (version >= 3) {
534 : 0 : buffer_put_cstring(&msg, status_to_message(status));
535 : 0 : buffer_put_cstring(&msg, "");
536 : : }
537 : 0 : send_msg(&msg);
538 : 0 : buffer_free(&msg);
539 : 0 : }
540 : : static void
541 : 0 : send_data_or_handle(char type, u_int32_t id, const char *data, int dlen)
542 : : {
543 : : Buffer msg;
544 : :
545 : 0 : buffer_init(&msg);
546 : 0 : buffer_put_char(&msg, type);
547 : 0 : buffer_put_int(&msg, id);
548 : 0 : buffer_put_string(&msg, data, dlen);
549 : 0 : send_msg(&msg);
550 : 0 : buffer_free(&msg);
551 : 0 : }
552 : :
553 : : static void
554 : 0 : send_data(u_int32_t id, const char *data, int dlen)
555 : : {
556 : 0 : debug("request %u: sent data len %d", id, dlen);
557 : 0 : send_data_or_handle(SSH2_FXP_DATA, id, data, dlen);
558 : 0 : }
559 : :
560 : : static void
561 : 0 : send_handle(u_int32_t id, int handle)
562 : : {
563 : : char *string;
564 : : int hlen;
565 : :
566 : 0 : handle_to_string(handle, &string, &hlen);
567 : 0 : debug("request %u: sent handle handle %d", id, handle);
568 : 0 : send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen);
569 : 0 : free(string);
570 : 0 : }
571 : :
572 : : static void
573 : 0 : send_names(u_int32_t id, int count, const Stat *stats)
574 : : {
575 : : Buffer msg;
576 : : int i;
577 : :
578 : 0 : buffer_init(&msg);
579 : 0 : buffer_put_char(&msg, SSH2_FXP_NAME);
580 : 0 : buffer_put_int(&msg, id);
581 : 0 : buffer_put_int(&msg, count);
582 : 0 : debug("request %u: sent names count %d", id, count);
583 [ # # ]: 0 : for (i = 0; i < count; i++) {
584 : 0 : buffer_put_cstring(&msg, stats[i].name);
585 : 0 : buffer_put_cstring(&msg, stats[i].long_name);
586 : 0 : encode_attrib(&msg, &stats[i].attrib);
587 : : }
588 : 0 : send_msg(&msg);
589 : 0 : buffer_free(&msg);
590 : 0 : }
591 : :
592 : : static void
593 : 0 : send_attrib(u_int32_t id, const Attrib *a)
594 : : {
595 : : Buffer msg;
596 : :
597 : 0 : debug("request %u: sent attrib have 0x%x", id, a->flags);
598 : 0 : buffer_init(&msg);
599 : 0 : buffer_put_char(&msg, SSH2_FXP_ATTRS);
600 : 0 : buffer_put_int(&msg, id);
601 : 0 : encode_attrib(&msg, a);
602 : 0 : send_msg(&msg);
603 : 0 : buffer_free(&msg);
604 : 0 : }
605 : :
606 : : static void
607 : 0 : send_statvfs(u_int32_t id, struct statvfs *st)
608 : : {
609 : : Buffer msg;
610 : : u_int64_t flag;
611 : :
612 : 0 : flag = (st->f_flag & ST_RDONLY) ? SSH2_FXE_STATVFS_ST_RDONLY : 0;
613 : 0 : flag |= (st->f_flag & ST_NOSUID) ? SSH2_FXE_STATVFS_ST_NOSUID : 0;
614 : :
615 : 0 : buffer_init(&msg);
616 : 0 : buffer_put_char(&msg, SSH2_FXP_EXTENDED_REPLY);
617 : 0 : buffer_put_int(&msg, id);
618 : 0 : buffer_put_int64(&msg, st->f_bsize);
619 : 0 : buffer_put_int64(&msg, st->f_frsize);
620 : 0 : buffer_put_int64(&msg, st->f_blocks);
621 : 0 : buffer_put_int64(&msg, st->f_bfree);
622 : 0 : buffer_put_int64(&msg, st->f_bavail);
623 : 0 : buffer_put_int64(&msg, st->f_files);
624 : 0 : buffer_put_int64(&msg, st->f_ffree);
625 : 0 : buffer_put_int64(&msg, st->f_favail);
626 : 0 : buffer_put_int64(&msg, FSID_TO_ULONG(st->f_fsid));
627 : 0 : buffer_put_int64(&msg, flag);
628 : 0 : buffer_put_int64(&msg, st->f_namemax);
629 : 0 : send_msg(&msg);
630 : 0 : buffer_free(&msg);
631 : 0 : }
632 : :
633 : : /* parse incoming */
634 : :
635 : : static void
636 : 0 : process_init(void)
637 : : {
638 : : Buffer msg;
639 : :
640 : 0 : version = get_int();
641 : 0 : verbose("received client version %u", version);
642 : 0 : buffer_init(&msg);
643 : 0 : buffer_put_char(&msg, SSH2_FXP_VERSION);
644 : 0 : buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
645 : : /* POSIX rename extension */
646 : 0 : buffer_put_cstring(&msg, "posix-rename@openssh.com");
647 : 0 : buffer_put_cstring(&msg, "1"); /* version */
648 : : /* statvfs extension */
649 : 0 : buffer_put_cstring(&msg, "statvfs@openssh.com");
650 : 0 : buffer_put_cstring(&msg, "2"); /* version */
651 : : /* fstatvfs extension */
652 : 0 : buffer_put_cstring(&msg, "fstatvfs@openssh.com");
653 : 0 : buffer_put_cstring(&msg, "2"); /* version */
654 : : /* hardlink extension */
655 : 0 : buffer_put_cstring(&msg, "hardlink@openssh.com");
656 : 0 : buffer_put_cstring(&msg, "1"); /* version */
657 : : /* fsync extension */
658 : 0 : buffer_put_cstring(&msg, "fsync@openssh.com");
659 : 0 : buffer_put_cstring(&msg, "1"); /* version */
660 : 0 : send_msg(&msg);
661 : 0 : buffer_free(&msg);
662 : 0 : }
663 : :
664 : : static void
665 : 0 : process_open(u_int32_t id)
666 : : {
667 : : u_int32_t pflags;
668 : : Attrib *a;
669 : : char *name;
670 : 0 : int handle, fd, flags, mode, status = SSH2_FX_FAILURE;
671 : :
672 : 0 : name = get_string(NULL);
673 : 0 : pflags = get_int(); /* portable flags */
674 : 0 : debug3("request %u: open flags %d", id, pflags);
675 : 0 : a = get_attrib();
676 : 0 : flags = flags_from_portable(pflags);
677 [ # # ]: 0 : mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666;
678 : 0 : logit("open \"%s\" flags %s mode 0%o",
679 : : name, string_from_portable(pflags), mode);
680 [ # # ][ # # ]: 0 : if (readonly &&
681 : 0 : ((flags & O_ACCMODE) == O_WRONLY ||
682 : : (flags & O_ACCMODE) == O_RDWR)) {
683 : 0 : verbose("Refusing open request in read-only mode");
684 : 0 : status = SSH2_FX_PERMISSION_DENIED;
685 : : } else {
686 : 0 : fd = open(name, flags, mode);
687 [ # # ]: 0 : if (fd < 0) {
688 : 0 : status = errno_to_portable(errno);
689 : : } else {
690 : 0 : handle = handle_new(HANDLE_FILE, name, fd, flags, NULL);
691 [ # # ]: 0 : if (handle < 0) {
692 : 0 : close(fd);
693 : : } else {
694 : 0 : send_handle(id, handle);
695 : 0 : status = SSH2_FX_OK;
696 : : }
697 : : }
698 : : }
699 [ # # ]: 0 : if (status != SSH2_FX_OK)
700 : 0 : send_status(id, status);
701 : 0 : free(name);
702 : 0 : }
703 : :
704 : : static void
705 : 0 : process_close(u_int32_t id)
706 : : {
707 : 0 : int handle, ret, status = SSH2_FX_FAILURE;
708 : :
709 : 0 : handle = get_handle();
710 : 0 : debug3("request %u: close handle %u", id, handle);
711 : 0 : handle_log_close(handle, NULL);
712 : 0 : ret = handle_close(handle);
713 [ # # ]: 0 : status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
714 : 0 : send_status(id, status);
715 : 0 : }
716 : :
717 : : static void
718 : 0 : process_read(u_int32_t id)
719 : : {
720 : : char buf[64*1024];
721 : : u_int32_t len;
722 : 0 : int handle, fd, ret, status = SSH2_FX_FAILURE;
723 : : u_int64_t off;
724 : :
725 : 0 : handle = get_handle();
726 : 0 : off = get_int64();
727 : 0 : len = get_int();
728 : :
729 : 0 : debug("request %u: read \"%s\" (handle %d) off %llu len %d",
730 : : id, handle_to_name(handle), handle, (unsigned long long)off, len);
731 [ # # ]: 0 : if (len > sizeof buf) {
732 : 0 : len = sizeof buf;
733 : 0 : debug2("read change len %d", len);
734 : : }
735 : 0 : fd = handle_to_fd(handle);
736 [ # # ]: 0 : if (fd >= 0) {
737 [ # # ]: 0 : if (lseek(fd, off, SEEK_SET) < 0) {
738 : 0 : error("process_read: seek failed");
739 : 0 : status = errno_to_portable(errno);
740 : : } else {
741 : 0 : ret = read(fd, buf, len);
742 [ # # ]: 0 : if (ret < 0) {
743 : 0 : status = errno_to_portable(errno);
744 [ # # ]: 0 : } else if (ret == 0) {
745 : : status = SSH2_FX_EOF;
746 : : } else {
747 : 0 : send_data(id, buf, ret);
748 : 0 : status = SSH2_FX_OK;
749 : 0 : handle_update_read(handle, ret);
750 : : }
751 : : }
752 : : }
753 [ # # ]: 0 : if (status != SSH2_FX_OK)
754 : 0 : send_status(id, status);
755 : 0 : }
756 : :
757 : : static void
758 : 0 : process_write(u_int32_t id)
759 : : {
760 : : u_int64_t off;
761 : : u_int len;
762 : : int handle, fd, ret, status;
763 : : char *data;
764 : :
765 : 0 : handle = get_handle();
766 : 0 : off = get_int64();
767 : 0 : data = get_string(&len);
768 : :
769 : 0 : debug("request %u: write \"%s\" (handle %d) off %llu len %d",
770 : : id, handle_to_name(handle), handle, (unsigned long long)off, len);
771 : 0 : fd = handle_to_fd(handle);
772 : :
773 [ # # ]: 0 : if (fd < 0)
774 : : status = SSH2_FX_FAILURE;
775 : : else {
776 [ # # # # ]: 0 : if (!(handle_to_flags(handle) & O_APPEND) &&
777 : 0 : lseek(fd, off, SEEK_SET) < 0) {
778 : 0 : status = errno_to_portable(errno);
779 : 0 : error("process_write: seek failed");
780 : : } else {
781 : : /* XXX ATOMICIO ? */
782 : 0 : ret = write(fd, data, len);
783 [ # # ]: 0 : if (ret < 0) {
784 : 0 : error("process_write: write failed");
785 : 0 : status = errno_to_portable(errno);
786 [ # # ]: 0 : } else if ((size_t)ret == len) {
787 : 0 : status = SSH2_FX_OK;
788 : 0 : handle_update_write(handle, ret);
789 : : } else {
790 : 0 : debug2("nothing at all written");
791 : 0 : status = SSH2_FX_FAILURE;
792 : : }
793 : : }
794 : : }
795 : 0 : send_status(id, status);
796 : 0 : free(data);
797 : 0 : }
798 : :
799 : : static void
800 : 0 : process_do_stat(u_int32_t id, int do_lstat)
801 : : {
802 : : Attrib a;
803 : : struct stat st;
804 : : char *name;
805 : 0 : int ret, status = SSH2_FX_FAILURE;
806 : :
807 : 0 : name = get_string(NULL);
808 [ # # ]: 0 : debug3("request %u: %sstat", id, do_lstat ? "l" : "");
809 [ # # ]: 0 : verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name);
810 [ # # ]: 0 : ret = do_lstat ? lstat(name, &st) : stat(name, &st);
811 [ # # ]: 0 : if (ret < 0) {
812 : 0 : status = errno_to_portable(errno);
813 : : } else {
814 : 0 : stat_to_attrib(&st, &a);
815 : 0 : send_attrib(id, &a);
816 : 0 : status = SSH2_FX_OK;
817 : : }
818 [ # # ]: 0 : if (status != SSH2_FX_OK)
819 : 0 : send_status(id, status);
820 : 0 : free(name);
821 : 0 : }
822 : :
823 : : static void
824 : 0 : process_stat(u_int32_t id)
825 : : {
826 : 0 : process_do_stat(id, 0);
827 : 0 : }
828 : :
829 : : static void
830 : 0 : process_lstat(u_int32_t id)
831 : : {
832 : 0 : process_do_stat(id, 1);
833 : 0 : }
834 : :
835 : : static void
836 : 0 : process_fstat(u_int32_t id)
837 : : {
838 : : Attrib a;
839 : : struct stat st;
840 : 0 : int fd, ret, handle, status = SSH2_FX_FAILURE;
841 : :
842 : 0 : handle = get_handle();
843 : 0 : debug("request %u: fstat \"%s\" (handle %u)",
844 : : id, handle_to_name(handle), handle);
845 : 0 : fd = handle_to_fd(handle);
846 [ # # ]: 0 : if (fd >= 0) {
847 : 0 : ret = fstat(fd, &st);
848 [ # # ]: 0 : if (ret < 0) {
849 : 0 : status = errno_to_portable(errno);
850 : : } else {
851 : 0 : stat_to_attrib(&st, &a);
852 : 0 : send_attrib(id, &a);
853 : 0 : status = SSH2_FX_OK;
854 : : }
855 : : }
856 [ # # ]: 0 : if (status != SSH2_FX_OK)
857 : 0 : send_status(id, status);
858 : 0 : }
859 : :
860 : : static struct timeval *
861 : : attrib_to_tv(const Attrib *a)
862 : : {
863 : : static struct timeval tv[2];
864 : :
865 : 0 : tv[0].tv_sec = a->atime;
866 : 0 : tv[0].tv_usec = 0;
867 : 0 : tv[1].tv_sec = a->mtime;
868 : 0 : tv[1].tv_usec = 0;
869 : : return tv;
870 : : }
871 : :
872 : : static void
873 : 0 : process_setstat(u_int32_t id)
874 : : {
875 : 0 : Attrib *a;
876 : : char *name;
877 : 0 : int status = SSH2_FX_OK, ret;
878 : :
879 : 0 : name = get_string(NULL);
880 : 0 : a = get_attrib();
881 : 0 : debug("request %u: setstat name \"%s\"", id, name);
882 [ # # ]: 0 : if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
883 : 0 : logit("set \"%s\" size %llu",
884 : 0 : name, (unsigned long long)a->size);
885 : 0 : ret = truncate(name, a->size);
886 [ # # ]: 0 : if (ret == -1)
887 : 0 : status = errno_to_portable(errno);
888 : : }
889 [ # # ]: 0 : if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
890 : 0 : logit("set \"%s\" mode %04o", name, a->perm);
891 : 0 : ret = chmod(name, a->perm & 07777);
892 [ # # ]: 0 : if (ret == -1)
893 : 0 : status = errno_to_portable(errno);
894 : : }
895 [ # # ]: 0 : if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
896 : : char buf[64];
897 : 0 : time_t t = a->mtime;
898 : :
899 : 0 : strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S",
900 : 0 : localtime(&t));
901 : 0 : logit("set \"%s\" modtime %s", name, buf);
902 : 0 : ret = utimes(name, attrib_to_tv(a));
903 [ # # ]: 0 : if (ret == -1)
904 : 0 : status = errno_to_portable(errno);
905 : : }
906 [ # # ]: 0 : if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
907 : 0 : logit("set \"%s\" owner %lu group %lu", name,
908 : 0 : (u_long)a->uid, (u_long)a->gid);
909 : 0 : ret = chown(name, a->uid, a->gid);
910 [ # # ]: 0 : if (ret == -1)
911 : 0 : status = errno_to_portable(errno);
912 : : }
913 : 0 : send_status(id, status);
914 : 0 : free(name);
915 : 0 : }
916 : :
917 : : static void
918 : 0 : process_fsetstat(u_int32_t id)
919 : : {
920 : 0 : Attrib *a;
921 : : int handle, fd, ret;
922 : 0 : int status = SSH2_FX_OK;
923 : :
924 : 0 : handle = get_handle();
925 : 0 : a = get_attrib();
926 : 0 : debug("request %u: fsetstat handle %d", id, handle);
927 : 0 : fd = handle_to_fd(handle);
928 [ # # ]: 0 : if (fd < 0)
929 : : status = SSH2_FX_FAILURE;
930 : : else {
931 : 0 : char *name = handle_to_name(handle);
932 : :
933 [ # # ]: 0 : if (a->flags & SSH2_FILEXFER_ATTR_SIZE) {
934 : 0 : logit("set \"%s\" size %llu",
935 : 0 : name, (unsigned long long)a->size);
936 : 0 : ret = ftruncate(fd, a->size);
937 [ # # ]: 0 : if (ret == -1)
938 : 0 : status = errno_to_portable(errno);
939 : : }
940 [ # # ]: 0 : if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
941 : 0 : logit("set \"%s\" mode %04o", name, a->perm);
942 : : #ifdef HAVE_FCHMOD
943 : 0 : ret = fchmod(fd, a->perm & 07777);
944 : : #else
945 : : ret = chmod(name, a->perm & 07777);
946 : : #endif
947 [ # # ]: 0 : if (ret == -1)
948 : 0 : status = errno_to_portable(errno);
949 : : }
950 [ # # ]: 0 : if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
951 : : char buf[64];
952 : 0 : time_t t = a->mtime;
953 : :
954 : 0 : strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S",
955 : 0 : localtime(&t));
956 : 0 : logit("set \"%s\" modtime %s", name, buf);
957 : : #ifdef HAVE_FUTIMES
958 : 0 : ret = futimes(fd, attrib_to_tv(a));
959 : : #else
960 : : ret = utimes(name, attrib_to_tv(a));
961 : : #endif
962 [ # # ]: 0 : if (ret == -1)
963 : 0 : status = errno_to_portable(errno);
964 : : }
965 [ # # ]: 0 : if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) {
966 : 0 : logit("set \"%s\" owner %lu group %lu", name,
967 : 0 : (u_long)a->uid, (u_long)a->gid);
968 : : #ifdef HAVE_FCHOWN
969 : 0 : ret = fchown(fd, a->uid, a->gid);
970 : : #else
971 : : ret = chown(name, a->uid, a->gid);
972 : : #endif
973 [ # # ]: 0 : if (ret == -1)
974 : 0 : status = errno_to_portable(errno);
975 : : }
976 : : }
977 : 0 : send_status(id, status);
978 : 0 : }
979 : :
980 : : static void
981 : 0 : process_opendir(u_int32_t id)
982 : : {
983 : 0 : DIR *dirp = NULL;
984 : : char *path;
985 : 0 : int handle, status = SSH2_FX_FAILURE;
986 : :
987 : 0 : path = get_string(NULL);
988 : 0 : debug3("request %u: opendir", id);
989 : 0 : logit("opendir \"%s\"", path);
990 : 0 : dirp = opendir(path);
991 [ # # ]: 0 : if (dirp == NULL) {
992 : 0 : status = errno_to_portable(errno);
993 : : } else {
994 : 0 : handle = handle_new(HANDLE_DIR, path, 0, 0, dirp);
995 [ # # ]: 0 : if (handle < 0) {
996 : 0 : closedir(dirp);
997 : : } else {
998 : 0 : send_handle(id, handle);
999 : 0 : status = SSH2_FX_OK;
1000 : : }
1001 : :
1002 : : }
1003 [ # # ]: 0 : if (status != SSH2_FX_OK)
1004 : 0 : send_status(id, status);
1005 : 0 : free(path);
1006 : 0 : }
1007 : :
1008 : : static void
1009 : 0 : process_readdir(u_int32_t id)
1010 : : {
1011 : : DIR *dirp;
1012 : : struct dirent *dp;
1013 : : char *path;
1014 : : int handle;
1015 : :
1016 : 0 : handle = get_handle();
1017 : 0 : debug("request %u: readdir \"%s\" (handle %d)", id,
1018 : : handle_to_name(handle), handle);
1019 : 0 : dirp = handle_to_dir(handle);
1020 : 0 : path = handle_to_name(handle);
1021 [ # # ]: 0 : if (dirp == NULL || path == NULL) {
1022 : 0 : send_status(id, SSH2_FX_FAILURE);
1023 : : } else {
1024 : : struct stat st;
1025 : : char pathname[MAXPATHLEN];
1026 : : Stat *stats;
1027 : 0 : int nstats = 10, count = 0, i;
1028 : :
1029 : 0 : stats = xcalloc(nstats, sizeof(Stat));
1030 [ # # ]: 0 : while ((dp = readdir(dirp)) != NULL) {
1031 [ # # ]: 0 : if (count >= nstats) {
1032 : 0 : nstats *= 2;
1033 : 0 : stats = xrealloc(stats, nstats, sizeof(Stat));
1034 : : }
1035 : : /* XXX OVERFLOW ? */
1036 [ # # ]: 0 : snprintf(pathname, sizeof pathname, "%s%s%s", path,
1037 [ # # ]: 0 : strcmp(path, "/") ? "/" : "", dp->d_name);
1038 [ # # ]: 0 : if (lstat(pathname, &st) < 0)
1039 : 0 : continue;
1040 : 0 : stat_to_attrib(&st, &(stats[count].attrib));
1041 : 0 : stats[count].name = xstrdup(dp->d_name);
1042 : 0 : stats[count].long_name = ls_file(dp->d_name, &st, 0, 0);
1043 : 0 : count++;
1044 : : /* send up to 100 entries in one message */
1045 : : /* XXX check packet size instead */
1046 [ # # ]: 0 : if (count == 100)
1047 : : break;
1048 : : }
1049 [ # # ]: 0 : if (count > 0) {
1050 : 0 : send_names(id, count, stats);
1051 [ # # ]: 0 : for (i = 0; i < count; i++) {
1052 : 0 : free(stats[i].name);
1053 : 0 : free(stats[i].long_name);
1054 : : }
1055 : : } else {
1056 : 0 : send_status(id, SSH2_FX_EOF);
1057 : : }
1058 : 0 : free(stats);
1059 : : }
1060 : 0 : }
1061 : :
1062 : : static void
1063 : 0 : process_remove(u_int32_t id)
1064 : : {
1065 : : char *name;
1066 : 0 : int status = SSH2_FX_FAILURE;
1067 : : int ret;
1068 : :
1069 : 0 : name = get_string(NULL);
1070 : 0 : debug3("request %u: remove", id);
1071 : 0 : logit("remove name \"%s\"", name);
1072 : 0 : ret = unlink(name);
1073 [ # # ]: 0 : status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1074 : 0 : send_status(id, status);
1075 : 0 : free(name);
1076 : 0 : }
1077 : :
1078 : : static void
1079 : 0 : process_mkdir(u_int32_t id)
1080 : : {
1081 : : Attrib *a;
1082 : : char *name;
1083 : 0 : int ret, mode, status = SSH2_FX_FAILURE;
1084 : :
1085 : 0 : name = get_string(NULL);
1086 : 0 : a = get_attrib();
1087 [ # # ]: 0 : mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
1088 : 0 : a->perm & 07777 : 0777;
1089 : 0 : debug3("request %u: mkdir", id);
1090 : 0 : logit("mkdir name \"%s\" mode 0%o", name, mode);
1091 : 0 : ret = mkdir(name, mode);
1092 [ # # ]: 0 : status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1093 : 0 : send_status(id, status);
1094 : 0 : free(name);
1095 : 0 : }
1096 : :
1097 : : static void
1098 : 0 : process_rmdir(u_int32_t id)
1099 : : {
1100 : : char *name;
1101 : : int ret, status;
1102 : :
1103 : 0 : name = get_string(NULL);
1104 : 0 : debug3("request %u: rmdir", id);
1105 : 0 : logit("rmdir name \"%s\"", name);
1106 : 0 : ret = rmdir(name);
1107 [ # # ]: 0 : status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1108 : 0 : send_status(id, status);
1109 : 0 : free(name);
1110 : 0 : }
1111 : :
1112 : : static void
1113 : 0 : process_realpath(u_int32_t id)
1114 : : {
1115 : : char resolvedname[MAXPATHLEN];
1116 : : char *path;
1117 : :
1118 : 0 : path = get_string(NULL);
1119 [ # # ]: 0 : if (path[0] == '\0') {
1120 : 0 : free(path);
1121 : 0 : path = xstrdup(".");
1122 : : }
1123 : 0 : debug3("request %u: realpath", id);
1124 : 0 : verbose("realpath \"%s\"", path);
1125 [ # # ]: 0 : if (realpath(path, resolvedname) == NULL) {
1126 : 0 : send_status(id, errno_to_portable(errno));
1127 : : } else {
1128 : : Stat s;
1129 : 0 : attrib_clear(&s.attrib);
1130 : 0 : s.name = s.long_name = resolvedname;
1131 : 0 : send_names(id, 1, &s);
1132 : : }
1133 : 0 : free(path);
1134 : 0 : }
1135 : :
1136 : : static void
1137 : 0 : process_rename(u_int32_t id)
1138 : : {
1139 : : char *oldpath, *newpath;
1140 : : int status;
1141 : : struct stat sb;
1142 : :
1143 : 0 : oldpath = get_string(NULL);
1144 : 0 : newpath = get_string(NULL);
1145 : 0 : debug3("request %u: rename", id);
1146 : 0 : logit("rename old \"%s\" new \"%s\"", oldpath, newpath);
1147 : 0 : status = SSH2_FX_FAILURE;
1148 [ # # ]: 0 : if (lstat(oldpath, &sb) == -1)
1149 : 0 : status = errno_to_portable(errno);
1150 [ # # ]: 0 : else if (S_ISREG(sb.st_mode)) {
1151 : : /* Race-free rename of regular files */
1152 [ # # ]: 0 : if (link(oldpath, newpath) == -1) {
1153 [ # # ]: 0 : if (errno == EOPNOTSUPP || errno == ENOSYS
1154 : : #ifdef EXDEV
1155 [ # # ]: 0 : || errno == EXDEV
1156 : : #endif
1157 : : #ifdef LINK_OPNOTSUPP_ERRNO
1158 [ # # ]: 0 : || errno == LINK_OPNOTSUPP_ERRNO
1159 : : #endif
1160 : 0 : ) {
1161 : : struct stat st;
1162 : :
1163 : : /*
1164 : : * fs doesn't support links, so fall back to
1165 : : * stat+rename. This is racy.
1166 : : */
1167 [ # # ]: 0 : if (stat(newpath, &st) == -1) {
1168 [ # # ]: 0 : if (rename(oldpath, newpath) == -1)
1169 : 0 : status =
1170 : 0 : errno_to_portable(errno);
1171 : : else
1172 : : status = SSH2_FX_OK;
1173 : : }
1174 : : } else {
1175 : 0 : status = errno_to_portable(errno);
1176 : : }
1177 [ # # ]: 0 : } else if (unlink(oldpath) == -1) {
1178 : 0 : status = errno_to_portable(errno);
1179 : : /* clean spare link */
1180 : 0 : unlink(newpath);
1181 : : } else
1182 : : status = SSH2_FX_OK;
1183 [ # # ]: 0 : } else if (stat(newpath, &sb) == -1) {
1184 [ # # ]: 0 : if (rename(oldpath, newpath) == -1)
1185 : 0 : status = errno_to_portable(errno);
1186 : : else
1187 : : status = SSH2_FX_OK;
1188 : : }
1189 : 0 : send_status(id, status);
1190 : 0 : free(oldpath);
1191 : 0 : free(newpath);
1192 : 0 : }
1193 : :
1194 : : static void
1195 : 0 : process_readlink(u_int32_t id)
1196 : : {
1197 : : int len;
1198 : : char buf[MAXPATHLEN];
1199 : : char *path;
1200 : :
1201 : 0 : path = get_string(NULL);
1202 : 0 : debug3("request %u: readlink", id);
1203 : 0 : verbose("readlink \"%s\"", path);
1204 [ # # ]: 0 : if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1)
1205 : 0 : send_status(id, errno_to_portable(errno));
1206 : : else {
1207 : : Stat s;
1208 : :
1209 : 0 : buf[len] = '\0';
1210 : 0 : attrib_clear(&s.attrib);
1211 : 0 : s.name = s.long_name = buf;
1212 : 0 : send_names(id, 1, &s);
1213 : : }
1214 : 0 : free(path);
1215 : 0 : }
1216 : :
1217 : : static void
1218 : 0 : process_symlink(u_int32_t id)
1219 : : {
1220 : : char *oldpath, *newpath;
1221 : : int ret, status;
1222 : :
1223 : 0 : oldpath = get_string(NULL);
1224 : 0 : newpath = get_string(NULL);
1225 : 0 : debug3("request %u: symlink", id);
1226 : 0 : logit("symlink old \"%s\" new \"%s\"", oldpath, newpath);
1227 : : /* this will fail if 'newpath' exists */
1228 : 0 : ret = symlink(oldpath, newpath);
1229 [ # # ]: 0 : status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1230 : 0 : send_status(id, status);
1231 : 0 : free(oldpath);
1232 : 0 : free(newpath);
1233 : 0 : }
1234 : :
1235 : : static void
1236 : 0 : process_extended_posix_rename(u_int32_t id)
1237 : : {
1238 : : char *oldpath, *newpath;
1239 : : int ret, status;
1240 : :
1241 : 0 : oldpath = get_string(NULL);
1242 : 0 : newpath = get_string(NULL);
1243 : 0 : debug3("request %u: posix-rename", id);
1244 : 0 : logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath);
1245 : 0 : ret = rename(oldpath, newpath);
1246 [ # # ]: 0 : status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1247 : 0 : send_status(id, status);
1248 : 0 : free(oldpath);
1249 : 0 : free(newpath);
1250 : 0 : }
1251 : :
1252 : : static void
1253 : 0 : process_extended_statvfs(u_int32_t id)
1254 : : {
1255 : : char *path;
1256 : : struct statvfs st;
1257 : :
1258 : 0 : path = get_string(NULL);
1259 : 0 : debug3("request %u: statvfs", id);
1260 : 0 : logit("statvfs \"%s\"", path);
1261 : :
1262 [ # # ]: 0 : if (statvfs(path, &st) != 0)
1263 : 0 : send_status(id, errno_to_portable(errno));
1264 : : else
1265 : 0 : send_statvfs(id, &st);
1266 : 0 : free(path);
1267 : 0 : }
1268 : :
1269 : : static void
1270 : 0 : process_extended_fstatvfs(u_int32_t id)
1271 : : {
1272 : : int handle, fd;
1273 : : struct statvfs st;
1274 : :
1275 : 0 : handle = get_handle();
1276 : 0 : debug("request %u: fstatvfs \"%s\" (handle %u)",
1277 : : id, handle_to_name(handle), handle);
1278 [ # # ]: 0 : if ((fd = handle_to_fd(handle)) < 0) {
1279 : 0 : send_status(id, SSH2_FX_FAILURE);
1280 : 0 : return;
1281 : : }
1282 [ # # ]: 0 : if (fstatvfs(fd, &st) != 0)
1283 : 0 : send_status(id, errno_to_portable(errno));
1284 : : else
1285 : 0 : send_statvfs(id, &st);
1286 : : }
1287 : :
1288 : : static void
1289 : 0 : process_extended_hardlink(u_int32_t id)
1290 : : {
1291 : : char *oldpath, *newpath;
1292 : : int ret, status;
1293 : :
1294 : 0 : oldpath = get_string(NULL);
1295 : 0 : newpath = get_string(NULL);
1296 : 0 : debug3("request %u: hardlink", id);
1297 : 0 : logit("hardlink old \"%s\" new \"%s\"", oldpath, newpath);
1298 : 0 : ret = link(oldpath, newpath);
1299 [ # # ]: 0 : status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1300 : 0 : send_status(id, status);
1301 : 0 : free(oldpath);
1302 : 0 : free(newpath);
1303 : 0 : }
1304 : :
1305 : : static void
1306 : 0 : process_extended_fsync(u_int32_t id)
1307 : : {
1308 : 0 : int handle, fd, ret, status = SSH2_FX_OP_UNSUPPORTED;
1309 : :
1310 : 0 : handle = get_handle();
1311 : 0 : debug3("request %u: fsync (handle %u)", id, handle);
1312 : 0 : verbose("fsync \"%s\"", handle_to_name(handle));
1313 [ # # ]: 0 : if ((fd = handle_to_fd(handle)) < 0)
1314 : : status = SSH2_FX_NO_SUCH_FILE;
1315 [ # # ]: 0 : else if (handle_is_ok(handle, HANDLE_FILE)) {
1316 : 0 : ret = fsync(fd);
1317 [ # # ]: 0 : status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK;
1318 : : }
1319 : 0 : send_status(id, status);
1320 : 0 : }
1321 : :
1322 : : static void
1323 : 0 : process_extended(u_int32_t id)
1324 : : {
1325 : : char *request;
1326 : : u_int i;
1327 : :
1328 : 0 : request = get_string(NULL);
1329 [ # # ]: 0 : for (i = 0; extended_handlers[i].handler != NULL; i++) {
1330 [ # # ]: 0 : if (strcmp(request, extended_handlers[i].ext_name) == 0) {
1331 [ # # ]: 0 : if (!request_permitted(&extended_handlers[i]))
1332 : 0 : send_status(id, SSH2_FX_PERMISSION_DENIED);
1333 : : else
1334 : 0 : extended_handlers[i].handler(id);
1335 : : break;
1336 : : }
1337 : : }
1338 [ # # ]: 0 : if (extended_handlers[i].handler == NULL) {
1339 : 0 : error("Unknown extended request \"%.100s\"", request);
1340 : 0 : send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
1341 : : }
1342 : 0 : free(request);
1343 : 0 : }
1344 : :
1345 : : /* stolen from ssh-agent */
1346 : :
1347 : : static void
1348 : 0 : process(void)
1349 : : {
1350 : : u_int msg_len, buf_len, consumed, type, i;
1351 : : u_char *cp;
1352 : : u_int32_t id;
1353 : :
1354 : 0 : buf_len = buffer_len(&iqueue);
1355 [ # # ]: 0 : if (buf_len < 5)
1356 : : return; /* Incomplete message. */
1357 : 0 : cp = buffer_ptr(&iqueue);
1358 : 0 : msg_len = get_u32(cp);
1359 [ # # ]: 0 : if (msg_len > SFTP_MAX_MSG_LENGTH) {
1360 : 0 : error("bad message from %s local user %s",
1361 : 0 : client_addr, pw->pw_name);
1362 : 0 : sftp_server_cleanup_exit(11);
1363 : : }
1364 [ # # ]: 0 : if (buf_len < msg_len + 4)
1365 : : return;
1366 : 0 : buffer_consume(&iqueue, 4);
1367 : 0 : buf_len -= 4;
1368 : 0 : type = buffer_get_char(&iqueue);
1369 : :
1370 [ # # # ]: 0 : switch (type) {
1371 : : case SSH2_FXP_INIT:
1372 : 0 : process_init();
1373 : 0 : init_done = 1;
1374 : 0 : break;
1375 : : case SSH2_FXP_EXTENDED:
1376 [ # # ]: 0 : if (!init_done)
1377 : 0 : fatal("Received extended request before init");
1378 : 0 : id = get_int();
1379 : 0 : process_extended(id);
1380 : 0 : break;
1381 : : default:
1382 [ # # ]: 0 : if (!init_done)
1383 : 0 : fatal("Received %u request before init", type);
1384 : 0 : id = get_int();
1385 [ # # ]: 0 : for (i = 0; handlers[i].handler != NULL; i++) {
1386 [ # # ]: 0 : if (type == handlers[i].type) {
1387 [ # # ]: 0 : if (!request_permitted(&handlers[i])) {
1388 : 0 : send_status(id,
1389 : : SSH2_FX_PERMISSION_DENIED);
1390 : : } else {
1391 : 0 : handlers[i].handler(id);
1392 : : }
1393 : : break;
1394 : : }
1395 : : }
1396 [ # # ]: 0 : if (handlers[i].handler == NULL)
1397 : 0 : error("Unknown message %u", type);
1398 : : }
1399 : : /* discard the remaining bytes from the current packet */
1400 [ # # ]: 0 : if (buf_len < buffer_len(&iqueue)) {
1401 : 0 : error("iqueue grew unexpectedly");
1402 : 0 : sftp_server_cleanup_exit(255);
1403 : : }
1404 : 0 : consumed = buf_len - buffer_len(&iqueue);
1405 [ # # ]: 0 : if (msg_len < consumed) {
1406 : 0 : error("msg_len %u < consumed %u", msg_len, consumed);
1407 : 0 : sftp_server_cleanup_exit(255);
1408 : : }
1409 [ # # ]: 0 : if (msg_len > consumed)
1410 : 0 : buffer_consume(&iqueue, msg_len - consumed);
1411 : : }
1412 : :
1413 : : /* Cleanup handler that logs active handles upon normal exit */
1414 : : void
1415 : 0 : sftp_server_cleanup_exit(int i)
1416 : : {
1417 [ # # ][ # # ]: 0 : if (pw != NULL && client_addr != NULL) {
1418 : 0 : handle_log_exit();
1419 : 0 : logit("session closed for local user %s from [%s]",
1420 : 0 : pw->pw_name, client_addr);
1421 : : }
1422 : 0 : _exit(i);
1423 : : }
1424 : :
1425 : : static void
1426 : 0 : sftp_server_usage(void)
1427 : : {
1428 : : extern char *__progname;
1429 : :
1430 : 0 : fprintf(stderr,
1431 : : "usage: %s [-ehR] [-d start_directory] [-f log_facility] "
1432 : : "[-l log_level]\n\t[-P blacklisted_requests] "
1433 : : "[-p whitelisted_requests] [-u umask]\n"
1434 : : " %s -Q protocol_feature\n",
1435 : : __progname, __progname);
1436 : 0 : exit(1);
1437 : : }
1438 : :
1439 : : int
1440 : 0 : sftp_server_main(int argc, char **argv, struct passwd *user_pw)
1441 : : {
1442 : : fd_set *rset, *wset;
1443 : 0 : int i, in, out, max, ch, skipargs = 0, log_stderr = 0;
1444 : : ssize_t len, olen, set_size;
1445 : 0 : SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
1446 : 0 : char *cp, *homedir = NULL, buf[4*4096];
1447 : : long mask;
1448 : :
1449 : : extern char *optarg;
1450 : : extern char *__progname;
1451 : :
1452 : 0 : __progname = ssh_get_progname(argv[0]);
1453 : 0 : log_init(__progname, log_level, log_facility, log_stderr);
1454 : :
1455 : 0 : pw = pwcopy(user_pw);
1456 : :
1457 [ # # ][ # # ]: 0 : while (!skipargs && (ch = getopt(argc, argv,
1458 : : "d:f:l:P:p:Q:u:cehR")) != -1) {
1459 [ # # # # : 0 : switch (ch) {
# # # # #
# # ]
1460 : : case 'Q':
1461 [ # # ]: 0 : if (strcasecmp(optarg, "requests") != 0) {
1462 : 0 : fprintf(stderr, "Invalid query type\n");
1463 : 0 : exit(1);
1464 : : }
1465 [ # # ]: 0 : for (i = 0; handlers[i].handler != NULL; i++)
1466 : 0 : printf("%s\n", handlers[i].name);
1467 [ # # ]: 0 : for (i = 0; extended_handlers[i].handler != NULL; i++)
1468 : 0 : printf("%s\n", extended_handlers[i].name);
1469 : 0 : exit(0);
1470 : : break;
1471 : : case 'R':
1472 : 0 : readonly = 1;
1473 : 0 : break;
1474 : : case 'c':
1475 : : /*
1476 : : * Ignore all arguments if we are invoked as a
1477 : : * shell using "sftp-server -c command"
1478 : : */
1479 : : skipargs = 1;
1480 : : break;
1481 : : case 'e':
1482 : 0 : log_stderr = 1;
1483 : 0 : break;
1484 : : case 'l':
1485 : 0 : log_level = log_level_number(optarg);
1486 [ # # ]: 0 : if (log_level == SYSLOG_LEVEL_NOT_SET)
1487 : 0 : error("Invalid log level \"%s\"", optarg);
1488 : : break;
1489 : : case 'f':
1490 : 0 : log_facility = log_facility_number(optarg);
1491 [ # # ]: 0 : if (log_facility == SYSLOG_FACILITY_NOT_SET)
1492 : 0 : error("Invalid log facility \"%s\"", optarg);
1493 : : break;
1494 : : case 'd':
1495 : 0 : cp = tilde_expand_filename(optarg, user_pw->pw_uid);
1496 : 0 : homedir = percent_expand(cp, "d", user_pw->pw_dir,
1497 : : "u", user_pw->pw_name, (char *)NULL);
1498 : 0 : free(cp);
1499 : 0 : break;
1500 : : case 'p':
1501 [ # # ]: 0 : if (request_whitelist != NULL)
1502 : 0 : fatal("Permitted requests already set");
1503 : 0 : request_whitelist = xstrdup(optarg);
1504 : 0 : break;
1505 : : case 'P':
1506 [ # # ]: 0 : if (request_blacklist != NULL)
1507 : 0 : fatal("Refused requests already set");
1508 : 0 : request_blacklist = xstrdup(optarg);
1509 : 0 : break;
1510 : : case 'u':
1511 : 0 : errno = 0;
1512 : 0 : mask = strtol(optarg, &cp, 8);
1513 [ # # ][ # # ]: 0 : if (mask < 0 || mask > 0777 || *cp != '\0' ||
[ # # ]
1514 [ # # ][ # # ]: 0 : cp == optarg || (mask == 0 && errno != 0))
1515 : 0 : fatal("Invalid umask \"%s\"", optarg);
1516 : 0 : (void)umask((mode_t)mask);
1517 : 0 : break;
1518 : : case 'h':
1519 : : default:
1520 : 0 : sftp_server_usage();
1521 : : }
1522 : : }
1523 : :
1524 : 0 : log_init(__progname, log_level, log_facility, log_stderr);
1525 : :
1526 [ # # ]: 0 : if ((cp = getenv("SSH_CONNECTION")) != NULL) {
1527 : 0 : client_addr = xstrdup(cp);
1528 [ # # ]: 0 : if ((cp = strchr(client_addr, ' ')) == NULL) {
1529 : 0 : error("Malformed SSH_CONNECTION variable: \"%s\"",
1530 : : getenv("SSH_CONNECTION"));
1531 : 0 : sftp_server_cleanup_exit(255);
1532 : : }
1533 : 0 : *cp = '\0';
1534 : : } else
1535 : 0 : client_addr = xstrdup("UNKNOWN");
1536 : :
1537 : 0 : logit("session opened for local user %s from [%s]",
1538 : 0 : pw->pw_name, client_addr);
1539 : :
1540 : 0 : in = STDIN_FILENO;
1541 : 0 : out = STDOUT_FILENO;
1542 : :
1543 : : #ifdef HAVE_CYGWIN
1544 : : setmode(in, O_BINARY);
1545 : : setmode(out, O_BINARY);
1546 : : #endif
1547 : :
1548 : 0 : max = 0;
1549 : : if (in > max)
1550 : : max = in;
1551 : : if (out > max)
1552 : : max = out;
1553 : :
1554 : 0 : buffer_init(&iqueue);
1555 : 0 : buffer_init(&oqueue);
1556 : :
1557 : 0 : set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask);
1558 : 0 : rset = (fd_set *)xmalloc(set_size);
1559 : 0 : wset = (fd_set *)xmalloc(set_size);
1560 : :
1561 [ # # ]: 0 : if (homedir != NULL) {
1562 [ # # ]: 0 : if (chdir(homedir) != 0) {
1563 : 0 : error("chdir to \"%s\" failed: %s", homedir,
1564 : 0 : strerror(errno));
1565 : : }
1566 : : }
1567 : :
1568 : : for (;;) {
1569 : 0 : memset(rset, 0, set_size);
1570 : 0 : memset(wset, 0, set_size);
1571 : :
1572 : : /*
1573 : : * Ensure that we can read a full buffer and handle
1574 : : * the worst-case length packet it can generate,
1575 : : * otherwise apply backpressure by stopping reads.
1576 : : */
1577 [ # # # # ]: 0 : if (buffer_check_alloc(&iqueue, sizeof(buf)) &&
1578 : 0 : buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH))
1579 : 0 : FD_SET(in, rset);
1580 : :
1581 : 0 : olen = buffer_len(&oqueue);
1582 [ # # ]: 0 : if (olen > 0)
1583 : 0 : FD_SET(out, wset);
1584 : :
1585 [ # # ]: 0 : if (select(max+1, rset, wset, NULL, NULL) < 0) {
1586 [ # # ]: 0 : if (errno == EINTR)
1587 : 0 : continue;
1588 : 0 : error("select: %s", strerror(errno));
1589 : 0 : sftp_server_cleanup_exit(2);
1590 : : }
1591 : :
1592 : : /* copy stdin to iqueue */
1593 [ # # ]: 0 : if (FD_ISSET(in, rset)) {
1594 : 0 : len = read(in, buf, sizeof buf);
1595 [ # # ]: 0 : if (len == 0) {
1596 : 0 : debug("read eof");
1597 : 0 : sftp_server_cleanup_exit(0);
1598 [ # # ]: 0 : } else if (len < 0) {
1599 : 0 : error("read: %s", strerror(errno));
1600 : 0 : sftp_server_cleanup_exit(1);
1601 : : } else {
1602 : 0 : buffer_append(&iqueue, buf, len);
1603 : : }
1604 : : }
1605 : : /* send oqueue to stdout */
1606 [ # # ]: 0 : if (FD_ISSET(out, wset)) {
1607 : 0 : len = write(out, buffer_ptr(&oqueue), olen);
1608 [ # # ]: 0 : if (len < 0) {
1609 : 0 : error("write: %s", strerror(errno));
1610 : 0 : sftp_server_cleanup_exit(1);
1611 : : } else {
1612 : 0 : buffer_consume(&oqueue, len);
1613 : : }
1614 : : }
1615 : :
1616 : : /*
1617 : : * Process requests from client if we can fit the results
1618 : : * into the output buffer, otherwise stop processing input
1619 : : * and let the output queue drain.
1620 : : */
1621 [ # # ]: 0 : if (buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH))
1622 : 0 : process();
1623 : : }
1624 : : }
|