17c478bd9Sstevel@tonic-gate /* 290685d2cSjp161948 * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. 390685d2cSjp161948 * 490685d2cSjp161948 * Permission to use, copy, modify, and distribute this software for any 590685d2cSjp161948 * purpose with or without fee is hereby granted, provided that the above 690685d2cSjp161948 * copyright notice and this permission notice appear in all copies. 790685d2cSjp161948 * 890685d2cSjp161948 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 990685d2cSjp161948 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1090685d2cSjp161948 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1190685d2cSjp161948 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1290685d2cSjp161948 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1390685d2cSjp161948 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1490685d2cSjp161948 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 157c478bd9Sstevel@tonic-gate */ 167c478bd9Sstevel@tonic-gate 1790685d2cSjp161948 /* $OpenBSD: sftp-server.c,v 1.71 2007/01/03 07:22:36 stevesk Exp $ */ 187c478bd9Sstevel@tonic-gate 1990685d2cSjp161948 #include "includes.h" 2090685d2cSjp161948 2190685d2cSjp161948 #include <sys/types.h> 2290685d2cSjp161948 #include <sys/param.h> 2390685d2cSjp161948 #include <sys/stat.h> 2490685d2cSjp161948 #ifdef HAVE_SYS_TIME_H 2590685d2cSjp161948 # include <sys/time.h> 2690685d2cSjp161948 #endif 2790685d2cSjp161948 2890685d2cSjp161948 #include <dirent.h> 2990685d2cSjp161948 #include <errno.h> 3090685d2cSjp161948 #include <fcntl.h> 3190685d2cSjp161948 #include <pwd.h> 3290685d2cSjp161948 #include <stdlib.h> 3390685d2cSjp161948 #include <stdio.h> 3490685d2cSjp161948 #include <string.h> 3590685d2cSjp161948 #include <pwd.h> 3690685d2cSjp161948 #include <time.h> 3790685d2cSjp161948 #include <unistd.h> 3890685d2cSjp161948 #include <stdarg.h> 3990685d2cSjp161948 4090685d2cSjp161948 #include "xmalloc.h" 417c478bd9Sstevel@tonic-gate #include "buffer.h" 427c478bd9Sstevel@tonic-gate #include "bufaux.h" 437c478bd9Sstevel@tonic-gate #include "log.h" 4490685d2cSjp161948 #include "misc.h" 4590685d2cSjp161948 #include "uidswap.h" 467c478bd9Sstevel@tonic-gate 477c478bd9Sstevel@tonic-gate #include "sftp.h" 487c478bd9Sstevel@tonic-gate #include "sftp-common.h" 497c478bd9Sstevel@tonic-gate 507c478bd9Sstevel@tonic-gate #ifdef HAVE___PROGNAME 517c478bd9Sstevel@tonic-gate extern char *__progname; 527c478bd9Sstevel@tonic-gate #else 537c478bd9Sstevel@tonic-gate char *__progname; 547c478bd9Sstevel@tonic-gate #endif 557c478bd9Sstevel@tonic-gate 5690685d2cSjp161948 /* helper */ 5790685d2cSjp161948 #define get_int64() buffer_get_int64(&iqueue); 5890685d2cSjp161948 #define get_int() buffer_get_int(&iqueue); 5990685d2cSjp161948 #define get_string(lenp) buffer_get_string(&iqueue, lenp); 6090685d2cSjp161948 6190685d2cSjp161948 static void cleanup_exit(int i); 6290685d2cSjp161948 6390685d2cSjp161948 /* Our verbosity */ 6490685d2cSjp161948 LogLevel log_level = SYSLOG_LEVEL_ERROR; 6590685d2cSjp161948 6690685d2cSjp161948 /* Our client */ 6790685d2cSjp161948 struct passwd *pw = NULL; 6890685d2cSjp161948 char *client_addr = NULL; 6990685d2cSjp161948 707c478bd9Sstevel@tonic-gate /* input and output queue */ 717c478bd9Sstevel@tonic-gate Buffer iqueue; 727c478bd9Sstevel@tonic-gate Buffer oqueue; 737c478bd9Sstevel@tonic-gate 747c478bd9Sstevel@tonic-gate /* Version of client */ 757c478bd9Sstevel@tonic-gate int version; 767c478bd9Sstevel@tonic-gate 7790685d2cSjp161948 /* portable attributes, etc. */ 787c478bd9Sstevel@tonic-gate 797c478bd9Sstevel@tonic-gate typedef struct Stat Stat; 807c478bd9Sstevel@tonic-gate 817c478bd9Sstevel@tonic-gate struct Stat { 827c478bd9Sstevel@tonic-gate char *name; 837c478bd9Sstevel@tonic-gate char *long_name; 847c478bd9Sstevel@tonic-gate Attrib attrib; 857c478bd9Sstevel@tonic-gate }; 867c478bd9Sstevel@tonic-gate 877c478bd9Sstevel@tonic-gate static int 887c478bd9Sstevel@tonic-gate errno_to_portable(int unixerrno) 897c478bd9Sstevel@tonic-gate { 907c478bd9Sstevel@tonic-gate int ret = 0; 917c478bd9Sstevel@tonic-gate 927c478bd9Sstevel@tonic-gate switch (unixerrno) { 937c478bd9Sstevel@tonic-gate case 0: 947c478bd9Sstevel@tonic-gate ret = SSH2_FX_OK; 957c478bd9Sstevel@tonic-gate break; 967c478bd9Sstevel@tonic-gate case ENOENT: 977c478bd9Sstevel@tonic-gate case ENOTDIR: 987c478bd9Sstevel@tonic-gate case EBADF: 997c478bd9Sstevel@tonic-gate case ELOOP: 1007c478bd9Sstevel@tonic-gate ret = SSH2_FX_NO_SUCH_FILE; 1017c478bd9Sstevel@tonic-gate break; 1027c478bd9Sstevel@tonic-gate case EPERM: 1037c478bd9Sstevel@tonic-gate case EACCES: 1047c478bd9Sstevel@tonic-gate case EFAULT: 1057c478bd9Sstevel@tonic-gate ret = SSH2_FX_PERMISSION_DENIED; 1067c478bd9Sstevel@tonic-gate break; 1077c478bd9Sstevel@tonic-gate case ENAMETOOLONG: 1087c478bd9Sstevel@tonic-gate case EINVAL: 1097c478bd9Sstevel@tonic-gate ret = SSH2_FX_BAD_MESSAGE; 1107c478bd9Sstevel@tonic-gate break; 1117c478bd9Sstevel@tonic-gate default: 1127c478bd9Sstevel@tonic-gate ret = SSH2_FX_FAILURE; 1137c478bd9Sstevel@tonic-gate break; 1147c478bd9Sstevel@tonic-gate } 1157c478bd9Sstevel@tonic-gate return ret; 1167c478bd9Sstevel@tonic-gate } 1177c478bd9Sstevel@tonic-gate 1187c478bd9Sstevel@tonic-gate static int 1197c478bd9Sstevel@tonic-gate flags_from_portable(int pflags) 1207c478bd9Sstevel@tonic-gate { 1217c478bd9Sstevel@tonic-gate int flags = 0; 1227c478bd9Sstevel@tonic-gate 1237c478bd9Sstevel@tonic-gate if ((pflags & SSH2_FXF_READ) && 1247c478bd9Sstevel@tonic-gate (pflags & SSH2_FXF_WRITE)) { 1257c478bd9Sstevel@tonic-gate flags = O_RDWR; 1267c478bd9Sstevel@tonic-gate } else if (pflags & SSH2_FXF_READ) { 1277c478bd9Sstevel@tonic-gate flags = O_RDONLY; 1287c478bd9Sstevel@tonic-gate } else if (pflags & SSH2_FXF_WRITE) { 1297c478bd9Sstevel@tonic-gate flags = O_WRONLY; 1307c478bd9Sstevel@tonic-gate } 1317c478bd9Sstevel@tonic-gate if (pflags & SSH2_FXF_CREAT) 1327c478bd9Sstevel@tonic-gate flags |= O_CREAT; 1337c478bd9Sstevel@tonic-gate if (pflags & SSH2_FXF_TRUNC) 1347c478bd9Sstevel@tonic-gate flags |= O_TRUNC; 1357c478bd9Sstevel@tonic-gate if (pflags & SSH2_FXF_EXCL) 1367c478bd9Sstevel@tonic-gate flags |= O_EXCL; 1377c478bd9Sstevel@tonic-gate return flags; 1387c478bd9Sstevel@tonic-gate } 1397c478bd9Sstevel@tonic-gate 14090685d2cSjp161948 static const char * 14190685d2cSjp161948 string_from_portable(int pflags) 14290685d2cSjp161948 { 14390685d2cSjp161948 static char ret[128]; 14490685d2cSjp161948 14590685d2cSjp161948 *ret = '\0'; 14690685d2cSjp161948 14790685d2cSjp161948 #define PAPPEND(str) { \ 14890685d2cSjp161948 if (*ret != '\0') \ 14990685d2cSjp161948 strlcat(ret, ",", sizeof(ret)); \ 15090685d2cSjp161948 strlcat(ret, str, sizeof(ret)); \ 15190685d2cSjp161948 } 15290685d2cSjp161948 15390685d2cSjp161948 if (pflags & SSH2_FXF_READ) 15490685d2cSjp161948 PAPPEND("READ") 15590685d2cSjp161948 if (pflags & SSH2_FXF_WRITE) 15690685d2cSjp161948 PAPPEND("WRITE") 15790685d2cSjp161948 if (pflags & SSH2_FXF_CREAT) 15890685d2cSjp161948 PAPPEND("CREATE") 15990685d2cSjp161948 if (pflags & SSH2_FXF_TRUNC) 16090685d2cSjp161948 PAPPEND("TRUNCATE") 16190685d2cSjp161948 if (pflags & SSH2_FXF_EXCL) 16290685d2cSjp161948 PAPPEND("EXCL") 16390685d2cSjp161948 16490685d2cSjp161948 return ret; 16590685d2cSjp161948 } 16690685d2cSjp161948 1677c478bd9Sstevel@tonic-gate static Attrib * 1687c478bd9Sstevel@tonic-gate get_attrib(void) 1697c478bd9Sstevel@tonic-gate { 1707c478bd9Sstevel@tonic-gate return decode_attrib(&iqueue); 1717c478bd9Sstevel@tonic-gate } 1727c478bd9Sstevel@tonic-gate 1737c478bd9Sstevel@tonic-gate /* handle handles */ 1747c478bd9Sstevel@tonic-gate 1757c478bd9Sstevel@tonic-gate typedef struct Handle Handle; 1767c478bd9Sstevel@tonic-gate struct Handle { 1777c478bd9Sstevel@tonic-gate int use; 1787c478bd9Sstevel@tonic-gate DIR *dirp; 1797c478bd9Sstevel@tonic-gate int fd; 1807c478bd9Sstevel@tonic-gate char *name; 18190685d2cSjp161948 u_int64_t bytes_read, bytes_write; 1827c478bd9Sstevel@tonic-gate }; 1837c478bd9Sstevel@tonic-gate 1847c478bd9Sstevel@tonic-gate enum { 1857c478bd9Sstevel@tonic-gate HANDLE_UNUSED, 1867c478bd9Sstevel@tonic-gate HANDLE_DIR, 1877c478bd9Sstevel@tonic-gate HANDLE_FILE 1887c478bd9Sstevel@tonic-gate }; 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate Handle handles[100]; 1917c478bd9Sstevel@tonic-gate 1927c478bd9Sstevel@tonic-gate static void 1937c478bd9Sstevel@tonic-gate handle_init(void) 1947c478bd9Sstevel@tonic-gate { 19590685d2cSjp161948 u_int i; 1967c478bd9Sstevel@tonic-gate 1977c478bd9Sstevel@tonic-gate for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) 1987c478bd9Sstevel@tonic-gate handles[i].use = HANDLE_UNUSED; 1997c478bd9Sstevel@tonic-gate } 2007c478bd9Sstevel@tonic-gate 2017c478bd9Sstevel@tonic-gate static int 20290685d2cSjp161948 handle_new(int use, const char *name, int fd, DIR *dirp) 2037c478bd9Sstevel@tonic-gate { 20490685d2cSjp161948 u_int i; 2057c478bd9Sstevel@tonic-gate 2067c478bd9Sstevel@tonic-gate for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) { 2077c478bd9Sstevel@tonic-gate if (handles[i].use == HANDLE_UNUSED) { 2087c478bd9Sstevel@tonic-gate handles[i].use = use; 2097c478bd9Sstevel@tonic-gate handles[i].dirp = dirp; 2107c478bd9Sstevel@tonic-gate handles[i].fd = fd; 21190685d2cSjp161948 handles[i].name = xstrdup(name); 21290685d2cSjp161948 handles[i].bytes_read = handles[i].bytes_write = 0; 2137c478bd9Sstevel@tonic-gate return i; 2147c478bd9Sstevel@tonic-gate } 2157c478bd9Sstevel@tonic-gate } 2167c478bd9Sstevel@tonic-gate return -1; 2177c478bd9Sstevel@tonic-gate } 2187c478bd9Sstevel@tonic-gate 2197c478bd9Sstevel@tonic-gate static int 2207c478bd9Sstevel@tonic-gate handle_is_ok(int i, int type) 2217c478bd9Sstevel@tonic-gate { 22290685d2cSjp161948 return i >= 0 && (u_int)i < sizeof(handles)/sizeof(Handle) && 2237c478bd9Sstevel@tonic-gate handles[i].use == type; 2247c478bd9Sstevel@tonic-gate } 2257c478bd9Sstevel@tonic-gate 2267c478bd9Sstevel@tonic-gate static int 2277c478bd9Sstevel@tonic-gate handle_to_string(int handle, char **stringp, int *hlenp) 2287c478bd9Sstevel@tonic-gate { 2297c478bd9Sstevel@tonic-gate if (stringp == NULL || hlenp == NULL) 2307c478bd9Sstevel@tonic-gate return -1; 2317c478bd9Sstevel@tonic-gate *stringp = xmalloc(sizeof(int32_t)); 23290685d2cSjp161948 put_u32(*stringp, handle); 2337c478bd9Sstevel@tonic-gate *hlenp = sizeof(int32_t); 2347c478bd9Sstevel@tonic-gate return 0; 2357c478bd9Sstevel@tonic-gate } 2367c478bd9Sstevel@tonic-gate 2377c478bd9Sstevel@tonic-gate static int 23890685d2cSjp161948 handle_from_string(const char *handle, u_int hlen) 2397c478bd9Sstevel@tonic-gate { 2407c478bd9Sstevel@tonic-gate int val; 2417c478bd9Sstevel@tonic-gate 2427c478bd9Sstevel@tonic-gate if (hlen != sizeof(int32_t)) 2437c478bd9Sstevel@tonic-gate return -1; 24490685d2cSjp161948 val = get_u32(handle); 2457c478bd9Sstevel@tonic-gate if (handle_is_ok(val, HANDLE_FILE) || 2467c478bd9Sstevel@tonic-gate handle_is_ok(val, HANDLE_DIR)) 2477c478bd9Sstevel@tonic-gate return val; 2487c478bd9Sstevel@tonic-gate return -1; 2497c478bd9Sstevel@tonic-gate } 2507c478bd9Sstevel@tonic-gate 2517c478bd9Sstevel@tonic-gate static char * 2527c478bd9Sstevel@tonic-gate handle_to_name(int handle) 2537c478bd9Sstevel@tonic-gate { 2547c478bd9Sstevel@tonic-gate if (handle_is_ok(handle, HANDLE_DIR)|| 2557c478bd9Sstevel@tonic-gate handle_is_ok(handle, HANDLE_FILE)) 2567c478bd9Sstevel@tonic-gate return handles[handle].name; 2577c478bd9Sstevel@tonic-gate return NULL; 2587c478bd9Sstevel@tonic-gate } 2597c478bd9Sstevel@tonic-gate 2607c478bd9Sstevel@tonic-gate static DIR * 2617c478bd9Sstevel@tonic-gate handle_to_dir(int handle) 2627c478bd9Sstevel@tonic-gate { 2637c478bd9Sstevel@tonic-gate if (handle_is_ok(handle, HANDLE_DIR)) 2647c478bd9Sstevel@tonic-gate return handles[handle].dirp; 2657c478bd9Sstevel@tonic-gate return NULL; 2667c478bd9Sstevel@tonic-gate } 2677c478bd9Sstevel@tonic-gate 2687c478bd9Sstevel@tonic-gate static int 2697c478bd9Sstevel@tonic-gate handle_to_fd(int handle) 2707c478bd9Sstevel@tonic-gate { 2717c478bd9Sstevel@tonic-gate if (handle_is_ok(handle, HANDLE_FILE)) 2727c478bd9Sstevel@tonic-gate return handles[handle].fd; 2737c478bd9Sstevel@tonic-gate return -1; 2747c478bd9Sstevel@tonic-gate } 2757c478bd9Sstevel@tonic-gate 27690685d2cSjp161948 static void 27790685d2cSjp161948 handle_update_read(int handle, ssize_t bytes) 27890685d2cSjp161948 { 27990685d2cSjp161948 if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 28090685d2cSjp161948 handles[handle].bytes_read += bytes; 28190685d2cSjp161948 } 28290685d2cSjp161948 28390685d2cSjp161948 static void 28490685d2cSjp161948 handle_update_write(int handle, ssize_t bytes) 28590685d2cSjp161948 { 28690685d2cSjp161948 if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 28790685d2cSjp161948 handles[handle].bytes_write += bytes; 28890685d2cSjp161948 } 28990685d2cSjp161948 29090685d2cSjp161948 static u_int64_t 29190685d2cSjp161948 handle_bytes_read(int handle) 29290685d2cSjp161948 { 29390685d2cSjp161948 if (handle_is_ok(handle, HANDLE_FILE)) 29490685d2cSjp161948 return (handles[handle].bytes_read); 29590685d2cSjp161948 return 0; 29690685d2cSjp161948 } 29790685d2cSjp161948 29890685d2cSjp161948 static u_int64_t 29990685d2cSjp161948 handle_bytes_write(int handle) 30090685d2cSjp161948 { 30190685d2cSjp161948 if (handle_is_ok(handle, HANDLE_FILE)) 30290685d2cSjp161948 return (handles[handle].bytes_write); 30390685d2cSjp161948 return 0; 30490685d2cSjp161948 } 30590685d2cSjp161948 3067c478bd9Sstevel@tonic-gate static int 3077c478bd9Sstevel@tonic-gate handle_close(int handle) 3087c478bd9Sstevel@tonic-gate { 3097c478bd9Sstevel@tonic-gate int ret = -1; 3107c478bd9Sstevel@tonic-gate 3117c478bd9Sstevel@tonic-gate if (handle_is_ok(handle, HANDLE_FILE)) { 3127c478bd9Sstevel@tonic-gate ret = close(handles[handle].fd); 3137c478bd9Sstevel@tonic-gate handles[handle].use = HANDLE_UNUSED; 31490685d2cSjp161948 xfree(handles[handle].name); 3157c478bd9Sstevel@tonic-gate } else if (handle_is_ok(handle, HANDLE_DIR)) { 3167c478bd9Sstevel@tonic-gate ret = closedir(handles[handle].dirp); 3177c478bd9Sstevel@tonic-gate handles[handle].use = HANDLE_UNUSED; 31890685d2cSjp161948 xfree(handles[handle].name); 3197c478bd9Sstevel@tonic-gate } else { 3207c478bd9Sstevel@tonic-gate errno = ENOENT; 3217c478bd9Sstevel@tonic-gate } 3227c478bd9Sstevel@tonic-gate return ret; 3237c478bd9Sstevel@tonic-gate } 3247c478bd9Sstevel@tonic-gate 32590685d2cSjp161948 static void 32690685d2cSjp161948 handle_log_close(int handle, char *emsg) 32790685d2cSjp161948 { 32890685d2cSjp161948 if (handle_is_ok(handle, HANDLE_FILE)) { 32990685d2cSjp161948 log("%s%sclose \"%s\" bytes read %llu written %llu", 33090685d2cSjp161948 emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 33190685d2cSjp161948 handle_to_name(handle), 33290685d2cSjp161948 (unsigned long long)handle_bytes_read(handle), 33390685d2cSjp161948 (unsigned long long)handle_bytes_write(handle)); 33490685d2cSjp161948 } else { 33590685d2cSjp161948 log("%s%sclosedir \"%s\"", 33690685d2cSjp161948 emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 33790685d2cSjp161948 handle_to_name(handle)); 33890685d2cSjp161948 } 33990685d2cSjp161948 } 34090685d2cSjp161948 34190685d2cSjp161948 static void 34290685d2cSjp161948 handle_log_exit(void) 34390685d2cSjp161948 { 34490685d2cSjp161948 u_int i; 34590685d2cSjp161948 34690685d2cSjp161948 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) 34790685d2cSjp161948 if (handles[i].use != HANDLE_UNUSED) 34890685d2cSjp161948 handle_log_close(i, "forced"); 34990685d2cSjp161948 } 35090685d2cSjp161948 3517c478bd9Sstevel@tonic-gate static int 3527c478bd9Sstevel@tonic-gate get_handle(void) 3537c478bd9Sstevel@tonic-gate { 3547c478bd9Sstevel@tonic-gate char *handle; 3557c478bd9Sstevel@tonic-gate int val = -1; 3567c478bd9Sstevel@tonic-gate u_int hlen; 3577c478bd9Sstevel@tonic-gate 3587c478bd9Sstevel@tonic-gate handle = get_string(&hlen); 3597c478bd9Sstevel@tonic-gate if (hlen < 256) 3607c478bd9Sstevel@tonic-gate val = handle_from_string(handle, hlen); 3617c478bd9Sstevel@tonic-gate xfree(handle); 3627c478bd9Sstevel@tonic-gate return val; 3637c478bd9Sstevel@tonic-gate } 3647c478bd9Sstevel@tonic-gate 3657c478bd9Sstevel@tonic-gate /* send replies */ 3667c478bd9Sstevel@tonic-gate 3677c478bd9Sstevel@tonic-gate static void 3687c478bd9Sstevel@tonic-gate send_msg(Buffer *m) 3697c478bd9Sstevel@tonic-gate { 3707c478bd9Sstevel@tonic-gate int mlen = buffer_len(m); 3717c478bd9Sstevel@tonic-gate 3727c478bd9Sstevel@tonic-gate buffer_put_int(&oqueue, mlen); 3737c478bd9Sstevel@tonic-gate buffer_append(&oqueue, buffer_ptr(m), mlen); 3747c478bd9Sstevel@tonic-gate buffer_consume(m, mlen); 3757c478bd9Sstevel@tonic-gate } 3767c478bd9Sstevel@tonic-gate 37790685d2cSjp161948 static const char * 37890685d2cSjp161948 status_to_message(u_int32_t status) 3797c478bd9Sstevel@tonic-gate { 3807c478bd9Sstevel@tonic-gate const char *status_messages[] = { 3817c478bd9Sstevel@tonic-gate "Success", /* SSH_FX_OK */ 3827c478bd9Sstevel@tonic-gate "End of file", /* SSH_FX_EOF */ 3837c478bd9Sstevel@tonic-gate "No such file", /* SSH_FX_NO_SUCH_FILE */ 3847c478bd9Sstevel@tonic-gate "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 3857c478bd9Sstevel@tonic-gate "Failure", /* SSH_FX_FAILURE */ 3867c478bd9Sstevel@tonic-gate "Bad message", /* SSH_FX_BAD_MESSAGE */ 3877c478bd9Sstevel@tonic-gate "No connection", /* SSH_FX_NO_CONNECTION */ 3887c478bd9Sstevel@tonic-gate "Connection lost", /* SSH_FX_CONNECTION_LOST */ 3897c478bd9Sstevel@tonic-gate "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 3907c478bd9Sstevel@tonic-gate "Unknown error" /* Others */ 3917c478bd9Sstevel@tonic-gate }; 39290685d2cSjp161948 return (status_messages[MIN(status,SSH2_FX_MAX)]); 39390685d2cSjp161948 } 3947c478bd9Sstevel@tonic-gate 39590685d2cSjp161948 static void 39690685d2cSjp161948 send_status(u_int32_t id, u_int32_t status) 39790685d2cSjp161948 { 39890685d2cSjp161948 Buffer msg; 39990685d2cSjp161948 40090685d2cSjp161948 debug3("request %u: sent status %u", id, status); 40190685d2cSjp161948 if (log_level > SYSLOG_LEVEL_VERBOSE || 40290685d2cSjp161948 (status != SSH2_FX_OK && status != SSH2_FX_EOF)) 40390685d2cSjp161948 log("sent status %s", status_to_message(status)); 4047c478bd9Sstevel@tonic-gate buffer_init(&msg); 4057c478bd9Sstevel@tonic-gate buffer_put_char(&msg, SSH2_FXP_STATUS); 4067c478bd9Sstevel@tonic-gate buffer_put_int(&msg, id); 40790685d2cSjp161948 buffer_put_int(&msg, status); 4087c478bd9Sstevel@tonic-gate if (version >= 3) { 40990685d2cSjp161948 buffer_put_cstring(&msg, status_to_message(status)); 4107c478bd9Sstevel@tonic-gate buffer_put_cstring(&msg, ""); 4117c478bd9Sstevel@tonic-gate } 4127c478bd9Sstevel@tonic-gate send_msg(&msg); 4137c478bd9Sstevel@tonic-gate buffer_free(&msg); 4147c478bd9Sstevel@tonic-gate } 4157c478bd9Sstevel@tonic-gate static void 41690685d2cSjp161948 send_data_or_handle(char type, u_int32_t id, const char *data, int dlen) 4177c478bd9Sstevel@tonic-gate { 4187c478bd9Sstevel@tonic-gate Buffer msg; 4197c478bd9Sstevel@tonic-gate 4207c478bd9Sstevel@tonic-gate buffer_init(&msg); 4217c478bd9Sstevel@tonic-gate buffer_put_char(&msg, type); 4227c478bd9Sstevel@tonic-gate buffer_put_int(&msg, id); 4237c478bd9Sstevel@tonic-gate buffer_put_string(&msg, data, dlen); 4247c478bd9Sstevel@tonic-gate send_msg(&msg); 4257c478bd9Sstevel@tonic-gate buffer_free(&msg); 4267c478bd9Sstevel@tonic-gate } 4277c478bd9Sstevel@tonic-gate 4287c478bd9Sstevel@tonic-gate static void 42990685d2cSjp161948 send_data(u_int32_t id, const char *data, int dlen) 4307c478bd9Sstevel@tonic-gate { 43190685d2cSjp161948 debug("request %u: sent data len %d", id, dlen); 4327c478bd9Sstevel@tonic-gate send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 4337c478bd9Sstevel@tonic-gate } 4347c478bd9Sstevel@tonic-gate 4357c478bd9Sstevel@tonic-gate static void 4367c478bd9Sstevel@tonic-gate send_handle(u_int32_t id, int handle) 4377c478bd9Sstevel@tonic-gate { 4387c478bd9Sstevel@tonic-gate char *string; 4397c478bd9Sstevel@tonic-gate int hlen; 4407c478bd9Sstevel@tonic-gate 4417c478bd9Sstevel@tonic-gate handle_to_string(handle, &string, &hlen); 44290685d2cSjp161948 debug("request %u: sent handle handle %d", id, handle); 4437c478bd9Sstevel@tonic-gate send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 4447c478bd9Sstevel@tonic-gate xfree(string); 4457c478bd9Sstevel@tonic-gate } 4467c478bd9Sstevel@tonic-gate 4477c478bd9Sstevel@tonic-gate static void 44890685d2cSjp161948 send_names(u_int32_t id, int count, const Stat *stats) 4497c478bd9Sstevel@tonic-gate { 4507c478bd9Sstevel@tonic-gate Buffer msg; 4517c478bd9Sstevel@tonic-gate int i; 4527c478bd9Sstevel@tonic-gate 4537c478bd9Sstevel@tonic-gate buffer_init(&msg); 4547c478bd9Sstevel@tonic-gate buffer_put_char(&msg, SSH2_FXP_NAME); 4557c478bd9Sstevel@tonic-gate buffer_put_int(&msg, id); 4567c478bd9Sstevel@tonic-gate buffer_put_int(&msg, count); 45790685d2cSjp161948 debug("request %u: sent names count %d", id, count); 4587c478bd9Sstevel@tonic-gate for (i = 0; i < count; i++) { 4597c478bd9Sstevel@tonic-gate buffer_put_cstring(&msg, stats[i].name); 4607c478bd9Sstevel@tonic-gate buffer_put_cstring(&msg, stats[i].long_name); 4617c478bd9Sstevel@tonic-gate encode_attrib(&msg, &stats[i].attrib); 4627c478bd9Sstevel@tonic-gate } 4637c478bd9Sstevel@tonic-gate send_msg(&msg); 4647c478bd9Sstevel@tonic-gate buffer_free(&msg); 4657c478bd9Sstevel@tonic-gate } 4667c478bd9Sstevel@tonic-gate 4677c478bd9Sstevel@tonic-gate static void 46890685d2cSjp161948 send_attrib(u_int32_t id, const Attrib *a) 4697c478bd9Sstevel@tonic-gate { 4707c478bd9Sstevel@tonic-gate Buffer msg; 4717c478bd9Sstevel@tonic-gate 47290685d2cSjp161948 debug("request %u: sent attrib have 0x%x", id, a->flags); 4737c478bd9Sstevel@tonic-gate buffer_init(&msg); 4747c478bd9Sstevel@tonic-gate buffer_put_char(&msg, SSH2_FXP_ATTRS); 4757c478bd9Sstevel@tonic-gate buffer_put_int(&msg, id); 4767c478bd9Sstevel@tonic-gate encode_attrib(&msg, a); 4777c478bd9Sstevel@tonic-gate send_msg(&msg); 4787c478bd9Sstevel@tonic-gate buffer_free(&msg); 4797c478bd9Sstevel@tonic-gate } 4807c478bd9Sstevel@tonic-gate 4817c478bd9Sstevel@tonic-gate /* parse incoming */ 4827c478bd9Sstevel@tonic-gate 4837c478bd9Sstevel@tonic-gate static void 4847c478bd9Sstevel@tonic-gate process_init(void) 4857c478bd9Sstevel@tonic-gate { 4867c478bd9Sstevel@tonic-gate Buffer msg; 4877c478bd9Sstevel@tonic-gate 4887c478bd9Sstevel@tonic-gate version = get_int(); 48990685d2cSjp161948 verbose("received client version %d", version); 4907c478bd9Sstevel@tonic-gate buffer_init(&msg); 4917c478bd9Sstevel@tonic-gate buffer_put_char(&msg, SSH2_FXP_VERSION); 4927c478bd9Sstevel@tonic-gate buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 4937c478bd9Sstevel@tonic-gate send_msg(&msg); 4947c478bd9Sstevel@tonic-gate buffer_free(&msg); 4957c478bd9Sstevel@tonic-gate } 4967c478bd9Sstevel@tonic-gate 4977c478bd9Sstevel@tonic-gate static void 4987c478bd9Sstevel@tonic-gate process_open(void) 4997c478bd9Sstevel@tonic-gate { 5007c478bd9Sstevel@tonic-gate u_int32_t id, pflags; 5017c478bd9Sstevel@tonic-gate Attrib *a; 5027c478bd9Sstevel@tonic-gate char *name; 5037c478bd9Sstevel@tonic-gate int handle, fd, flags, mode, status = SSH2_FX_FAILURE; 5047c478bd9Sstevel@tonic-gate 5057c478bd9Sstevel@tonic-gate id = get_int(); 5067c478bd9Sstevel@tonic-gate name = get_string(NULL); 5077c478bd9Sstevel@tonic-gate pflags = get_int(); /* portable flags */ 50890685d2cSjp161948 debug3("request %u: open flags %d", id, pflags); 5097c478bd9Sstevel@tonic-gate a = get_attrib(); 5107c478bd9Sstevel@tonic-gate flags = flags_from_portable(pflags); 5117c478bd9Sstevel@tonic-gate mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; 51290685d2cSjp161948 log("open \"%s\" flags %s mode 0%o", 51390685d2cSjp161948 name, string_from_portable(pflags), mode); 5147c478bd9Sstevel@tonic-gate fd = open(name, flags, mode); 5157c478bd9Sstevel@tonic-gate if (fd < 0) { 5167c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 5177c478bd9Sstevel@tonic-gate } else { 51890685d2cSjp161948 handle = handle_new(HANDLE_FILE, name, fd, NULL); 5197c478bd9Sstevel@tonic-gate if (handle < 0) { 5207c478bd9Sstevel@tonic-gate close(fd); 5217c478bd9Sstevel@tonic-gate } else { 5227c478bd9Sstevel@tonic-gate send_handle(id, handle); 5237c478bd9Sstevel@tonic-gate status = SSH2_FX_OK; 5247c478bd9Sstevel@tonic-gate } 5257c478bd9Sstevel@tonic-gate } 5267c478bd9Sstevel@tonic-gate if (status != SSH2_FX_OK) 5277c478bd9Sstevel@tonic-gate send_status(id, status); 5287c478bd9Sstevel@tonic-gate xfree(name); 5297c478bd9Sstevel@tonic-gate } 5307c478bd9Sstevel@tonic-gate 5317c478bd9Sstevel@tonic-gate static void 5327c478bd9Sstevel@tonic-gate process_close(void) 5337c478bd9Sstevel@tonic-gate { 5347c478bd9Sstevel@tonic-gate u_int32_t id; 5357c478bd9Sstevel@tonic-gate int handle, ret, status = SSH2_FX_FAILURE; 5367c478bd9Sstevel@tonic-gate 5377c478bd9Sstevel@tonic-gate id = get_int(); 5387c478bd9Sstevel@tonic-gate handle = get_handle(); 53990685d2cSjp161948 debug3("request %u: close handle %u", id, handle); 54090685d2cSjp161948 handle_log_close(handle, NULL); 5417c478bd9Sstevel@tonic-gate ret = handle_close(handle); 5427c478bd9Sstevel@tonic-gate status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 5437c478bd9Sstevel@tonic-gate send_status(id, status); 5447c478bd9Sstevel@tonic-gate } 5457c478bd9Sstevel@tonic-gate 5467c478bd9Sstevel@tonic-gate static void 5477c478bd9Sstevel@tonic-gate process_read(void) 5487c478bd9Sstevel@tonic-gate { 5497c478bd9Sstevel@tonic-gate char buf[64*1024]; 5507c478bd9Sstevel@tonic-gate u_int32_t id, len; 5517c478bd9Sstevel@tonic-gate int handle, fd, ret, status = SSH2_FX_FAILURE; 5527c478bd9Sstevel@tonic-gate u_int64_t off; 5537c478bd9Sstevel@tonic-gate 5547c478bd9Sstevel@tonic-gate id = get_int(); 5557c478bd9Sstevel@tonic-gate handle = get_handle(); 5567c478bd9Sstevel@tonic-gate off = get_int64(); 5577c478bd9Sstevel@tonic-gate len = get_int(); 5587c478bd9Sstevel@tonic-gate 55990685d2cSjp161948 debug("request %u: read \"%s\" (handle %d) off %llu len %d", 56090685d2cSjp161948 id, handle_to_name(handle), handle, (unsigned long long)off, len); 5617c478bd9Sstevel@tonic-gate if (len > sizeof buf) { 5627c478bd9Sstevel@tonic-gate len = sizeof buf; 56390685d2cSjp161948 debug2("read change len %d", len); 5647c478bd9Sstevel@tonic-gate } 5657c478bd9Sstevel@tonic-gate fd = handle_to_fd(handle); 5667c478bd9Sstevel@tonic-gate if (fd >= 0) { 5677c478bd9Sstevel@tonic-gate if (lseek(fd, off, SEEK_SET) < 0) { 5687c478bd9Sstevel@tonic-gate error("process_read: seek failed"); 5697c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 5707c478bd9Sstevel@tonic-gate } else { 5717c478bd9Sstevel@tonic-gate ret = read(fd, buf, len); 5727c478bd9Sstevel@tonic-gate if (ret < 0) { 5737c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 5747c478bd9Sstevel@tonic-gate } else if (ret == 0) { 5757c478bd9Sstevel@tonic-gate status = SSH2_FX_EOF; 5767c478bd9Sstevel@tonic-gate } else { 5777c478bd9Sstevel@tonic-gate send_data(id, buf, ret); 5787c478bd9Sstevel@tonic-gate status = SSH2_FX_OK; 57990685d2cSjp161948 handle_update_read(handle, ret); 5807c478bd9Sstevel@tonic-gate } 5817c478bd9Sstevel@tonic-gate } 5827c478bd9Sstevel@tonic-gate } 5837c478bd9Sstevel@tonic-gate if (status != SSH2_FX_OK) 5847c478bd9Sstevel@tonic-gate send_status(id, status); 5857c478bd9Sstevel@tonic-gate } 5867c478bd9Sstevel@tonic-gate 5877c478bd9Sstevel@tonic-gate static void 5887c478bd9Sstevel@tonic-gate process_write(void) 5897c478bd9Sstevel@tonic-gate { 5907c478bd9Sstevel@tonic-gate u_int32_t id; 5917c478bd9Sstevel@tonic-gate u_int64_t off; 5927c478bd9Sstevel@tonic-gate u_int len; 5937c478bd9Sstevel@tonic-gate int handle, fd, ret, status = SSH2_FX_FAILURE; 5947c478bd9Sstevel@tonic-gate char *data; 5957c478bd9Sstevel@tonic-gate 5967c478bd9Sstevel@tonic-gate id = get_int(); 5977c478bd9Sstevel@tonic-gate handle = get_handle(); 5987c478bd9Sstevel@tonic-gate off = get_int64(); 5997c478bd9Sstevel@tonic-gate data = get_string(&len); 6007c478bd9Sstevel@tonic-gate 60190685d2cSjp161948 debug("request %u: write \"%s\" (handle %d) off %llu len %d", 60290685d2cSjp161948 id, handle_to_name(handle), handle, (unsigned long long)off, len); 6037c478bd9Sstevel@tonic-gate fd = handle_to_fd(handle); 6047c478bd9Sstevel@tonic-gate if (fd >= 0) { 6057c478bd9Sstevel@tonic-gate if (lseek(fd, off, SEEK_SET) < 0) { 6067c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 6077c478bd9Sstevel@tonic-gate error("process_write: seek failed"); 6087c478bd9Sstevel@tonic-gate } else { 6097c478bd9Sstevel@tonic-gate /* XXX ATOMICIO ? */ 6107c478bd9Sstevel@tonic-gate ret = write(fd, data, len); 61190685d2cSjp161948 if (ret < 0) { 6127c478bd9Sstevel@tonic-gate error("process_write: write failed"); 6137c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 61490685d2cSjp161948 } else if ((size_t)ret == len) { 6157c478bd9Sstevel@tonic-gate status = SSH2_FX_OK; 61690685d2cSjp161948 handle_update_write(handle, ret); 6177c478bd9Sstevel@tonic-gate } else { 61890685d2cSjp161948 debug2("nothing at all written"); 6197c478bd9Sstevel@tonic-gate } 6207c478bd9Sstevel@tonic-gate } 6217c478bd9Sstevel@tonic-gate } 6227c478bd9Sstevel@tonic-gate send_status(id, status); 6237c478bd9Sstevel@tonic-gate xfree(data); 6247c478bd9Sstevel@tonic-gate } 6257c478bd9Sstevel@tonic-gate 6267c478bd9Sstevel@tonic-gate static void 6277c478bd9Sstevel@tonic-gate process_do_stat(int do_lstat) 6287c478bd9Sstevel@tonic-gate { 6297c478bd9Sstevel@tonic-gate Attrib a; 6307c478bd9Sstevel@tonic-gate struct stat st; 6317c478bd9Sstevel@tonic-gate u_int32_t id; 6327c478bd9Sstevel@tonic-gate char *name; 6337c478bd9Sstevel@tonic-gate int ret, status = SSH2_FX_FAILURE; 6347c478bd9Sstevel@tonic-gate 6357c478bd9Sstevel@tonic-gate id = get_int(); 6367c478bd9Sstevel@tonic-gate name = get_string(NULL); 63790685d2cSjp161948 debug3("request %u: %sstat", id, do_lstat ? "l" : ""); 63890685d2cSjp161948 verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); 6397c478bd9Sstevel@tonic-gate ret = do_lstat ? lstat(name, &st) : stat(name, &st); 6407c478bd9Sstevel@tonic-gate if (ret < 0) { 6417c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 6427c478bd9Sstevel@tonic-gate } else { 6437c478bd9Sstevel@tonic-gate stat_to_attrib(&st, &a); 6447c478bd9Sstevel@tonic-gate send_attrib(id, &a); 6457c478bd9Sstevel@tonic-gate status = SSH2_FX_OK; 6467c478bd9Sstevel@tonic-gate } 6477c478bd9Sstevel@tonic-gate if (status != SSH2_FX_OK) 6487c478bd9Sstevel@tonic-gate send_status(id, status); 6497c478bd9Sstevel@tonic-gate xfree(name); 6507c478bd9Sstevel@tonic-gate } 6517c478bd9Sstevel@tonic-gate 6527c478bd9Sstevel@tonic-gate static void 6537c478bd9Sstevel@tonic-gate process_stat(void) 6547c478bd9Sstevel@tonic-gate { 6557c478bd9Sstevel@tonic-gate process_do_stat(0); 6567c478bd9Sstevel@tonic-gate } 6577c478bd9Sstevel@tonic-gate 6587c478bd9Sstevel@tonic-gate static void 6597c478bd9Sstevel@tonic-gate process_lstat(void) 6607c478bd9Sstevel@tonic-gate { 6617c478bd9Sstevel@tonic-gate process_do_stat(1); 6627c478bd9Sstevel@tonic-gate } 6637c478bd9Sstevel@tonic-gate 6647c478bd9Sstevel@tonic-gate static void 6657c478bd9Sstevel@tonic-gate process_fstat(void) 6667c478bd9Sstevel@tonic-gate { 6677c478bd9Sstevel@tonic-gate Attrib a; 6687c478bd9Sstevel@tonic-gate struct stat st; 6697c478bd9Sstevel@tonic-gate u_int32_t id; 6707c478bd9Sstevel@tonic-gate int fd, ret, handle, status = SSH2_FX_FAILURE; 6717c478bd9Sstevel@tonic-gate 6727c478bd9Sstevel@tonic-gate id = get_int(); 6737c478bd9Sstevel@tonic-gate handle = get_handle(); 67490685d2cSjp161948 debug("request %u: fstat \"%s\" (handle %u)", 67590685d2cSjp161948 id, handle_to_name(handle), handle); 6767c478bd9Sstevel@tonic-gate fd = handle_to_fd(handle); 6777c478bd9Sstevel@tonic-gate if (fd >= 0) { 6787c478bd9Sstevel@tonic-gate ret = fstat(fd, &st); 6797c478bd9Sstevel@tonic-gate if (ret < 0) { 6807c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 6817c478bd9Sstevel@tonic-gate } else { 6827c478bd9Sstevel@tonic-gate stat_to_attrib(&st, &a); 6837c478bd9Sstevel@tonic-gate send_attrib(id, &a); 6847c478bd9Sstevel@tonic-gate status = SSH2_FX_OK; 6857c478bd9Sstevel@tonic-gate } 6867c478bd9Sstevel@tonic-gate } 6877c478bd9Sstevel@tonic-gate if (status != SSH2_FX_OK) 6887c478bd9Sstevel@tonic-gate send_status(id, status); 6897c478bd9Sstevel@tonic-gate } 6907c478bd9Sstevel@tonic-gate 6917c478bd9Sstevel@tonic-gate static struct timeval * 69290685d2cSjp161948 attrib_to_tv(const Attrib *a) 6937c478bd9Sstevel@tonic-gate { 6947c478bd9Sstevel@tonic-gate static struct timeval tv[2]; 6957c478bd9Sstevel@tonic-gate 6967c478bd9Sstevel@tonic-gate tv[0].tv_sec = a->atime; 6977c478bd9Sstevel@tonic-gate tv[0].tv_usec = 0; 6987c478bd9Sstevel@tonic-gate tv[1].tv_sec = a->mtime; 6997c478bd9Sstevel@tonic-gate tv[1].tv_usec = 0; 7007c478bd9Sstevel@tonic-gate return tv; 7017c478bd9Sstevel@tonic-gate } 7027c478bd9Sstevel@tonic-gate 7037c478bd9Sstevel@tonic-gate static void 7047c478bd9Sstevel@tonic-gate process_setstat(void) 7057c478bd9Sstevel@tonic-gate { 7067c478bd9Sstevel@tonic-gate Attrib *a; 7077c478bd9Sstevel@tonic-gate u_int32_t id; 7087c478bd9Sstevel@tonic-gate char *name; 7097c478bd9Sstevel@tonic-gate int status = SSH2_FX_OK, ret; 7107c478bd9Sstevel@tonic-gate 7117c478bd9Sstevel@tonic-gate id = get_int(); 7127c478bd9Sstevel@tonic-gate name = get_string(NULL); 7137c478bd9Sstevel@tonic-gate a = get_attrib(); 71490685d2cSjp161948 debug("request %u: setstat name \"%s\"", id, name); 7157c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 71690685d2cSjp161948 log("set \"%s\" size %llu", 71790685d2cSjp161948 name, (unsigned long long)a->size); 7187c478bd9Sstevel@tonic-gate ret = truncate(name, a->size); 7197c478bd9Sstevel@tonic-gate if (ret == -1) 7207c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 7217c478bd9Sstevel@tonic-gate } 7227c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 72390685d2cSjp161948 log("set \"%s\" mode %04o", name, a->perm); 724*9c68f910SVladimir Kotal ret = chmod(name, a->perm & 07777); 7257c478bd9Sstevel@tonic-gate if (ret == -1) 7267c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 7277c478bd9Sstevel@tonic-gate } 7287c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 72990685d2cSjp161948 char buf[64]; 73090685d2cSjp161948 time_t t = a->mtime; 73190685d2cSjp161948 73290685d2cSjp161948 strftime(buf, sizeof(buf), "%Y" "%m%d-%H:%M:%S", 73390685d2cSjp161948 localtime(&t)); 73490685d2cSjp161948 log("set \"%s\" modtime %s", name, buf); 7357c478bd9Sstevel@tonic-gate ret = utimes(name, attrib_to_tv(a)); 7367c478bd9Sstevel@tonic-gate if (ret == -1) 7377c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 7387c478bd9Sstevel@tonic-gate } 7397c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 74090685d2cSjp161948 log("set \"%s\" owner %lu group %lu", name, 74190685d2cSjp161948 (u_long)a->uid, (u_long)a->gid); 7427c478bd9Sstevel@tonic-gate ret = chown(name, a->uid, a->gid); 7437c478bd9Sstevel@tonic-gate if (ret == -1) 7447c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 7457c478bd9Sstevel@tonic-gate } 7467c478bd9Sstevel@tonic-gate send_status(id, status); 7477c478bd9Sstevel@tonic-gate xfree(name); 7487c478bd9Sstevel@tonic-gate } 7497c478bd9Sstevel@tonic-gate 7507c478bd9Sstevel@tonic-gate static void 7517c478bd9Sstevel@tonic-gate process_fsetstat(void) 7527c478bd9Sstevel@tonic-gate { 7537c478bd9Sstevel@tonic-gate Attrib *a; 7547c478bd9Sstevel@tonic-gate u_int32_t id; 7557c478bd9Sstevel@tonic-gate int handle, fd, ret; 7567c478bd9Sstevel@tonic-gate int status = SSH2_FX_OK; 7577c478bd9Sstevel@tonic-gate 7587c478bd9Sstevel@tonic-gate id = get_int(); 7597c478bd9Sstevel@tonic-gate handle = get_handle(); 7607c478bd9Sstevel@tonic-gate a = get_attrib(); 76190685d2cSjp161948 debug("request %u: fsetstat handle %d", id, handle); 7627c478bd9Sstevel@tonic-gate fd = handle_to_fd(handle); 76390685d2cSjp161948 if (fd < 0) { 7647c478bd9Sstevel@tonic-gate status = SSH2_FX_FAILURE; 7657c478bd9Sstevel@tonic-gate } else { 76690685d2cSjp161948 char *name = handle_to_name(handle); 76790685d2cSjp161948 7687c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 76990685d2cSjp161948 log("set \"%s\" size %llu", 77090685d2cSjp161948 name, (unsigned long long)a->size); 7717c478bd9Sstevel@tonic-gate ret = ftruncate(fd, a->size); 7727c478bd9Sstevel@tonic-gate if (ret == -1) 7737c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 7747c478bd9Sstevel@tonic-gate } 7757c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 77690685d2cSjp161948 log("set \"%s\" mode %04o", name, a->perm); 7777c478bd9Sstevel@tonic-gate #ifdef HAVE_FCHMOD 778*9c68f910SVladimir Kotal ret = fchmod(fd, a->perm & 07777); 7797c478bd9Sstevel@tonic-gate #else 780*9c68f910SVladimir Kotal ret = chmod(name, a->perm & 07777); 7817c478bd9Sstevel@tonic-gate #endif 7827c478bd9Sstevel@tonic-gate if (ret == -1) 7837c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 7847c478bd9Sstevel@tonic-gate } 7857c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 78690685d2cSjp161948 char buf[64]; 78790685d2cSjp161948 time_t t = a->mtime; 78890685d2cSjp161948 78990685d2cSjp161948 strftime(buf, sizeof(buf), "%Y" "%m%d-%H:%M:%S", 79090685d2cSjp161948 localtime(&t)); 79190685d2cSjp161948 log("set \"%s\" modtime %s", name, buf); 7927c478bd9Sstevel@tonic-gate #ifdef HAVE_FUTIMES 7937c478bd9Sstevel@tonic-gate ret = futimes(fd, attrib_to_tv(a)); 7947c478bd9Sstevel@tonic-gate #else 7957c478bd9Sstevel@tonic-gate ret = utimes(name, attrib_to_tv(a)); 7967c478bd9Sstevel@tonic-gate #endif 7977c478bd9Sstevel@tonic-gate if (ret == -1) 7987c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 7997c478bd9Sstevel@tonic-gate } 8007c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 80190685d2cSjp161948 log("set \"%s\" owner %lu group %lu", name, 80290685d2cSjp161948 (u_long)a->uid, (u_long)a->gid); 8037c478bd9Sstevel@tonic-gate #ifdef HAVE_FCHOWN 8047c478bd9Sstevel@tonic-gate ret = fchown(fd, a->uid, a->gid); 8057c478bd9Sstevel@tonic-gate #else 8067c478bd9Sstevel@tonic-gate ret = chown(name, a->uid, a->gid); 8077c478bd9Sstevel@tonic-gate #endif 8087c478bd9Sstevel@tonic-gate if (ret == -1) 8097c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 8107c478bd9Sstevel@tonic-gate } 8117c478bd9Sstevel@tonic-gate } 8127c478bd9Sstevel@tonic-gate send_status(id, status); 8137c478bd9Sstevel@tonic-gate } 8147c478bd9Sstevel@tonic-gate 8157c478bd9Sstevel@tonic-gate static void 8167c478bd9Sstevel@tonic-gate process_opendir(void) 8177c478bd9Sstevel@tonic-gate { 8187c478bd9Sstevel@tonic-gate DIR *dirp = NULL; 8197c478bd9Sstevel@tonic-gate char *path; 8207c478bd9Sstevel@tonic-gate int handle, status = SSH2_FX_FAILURE; 8217c478bd9Sstevel@tonic-gate u_int32_t id; 8227c478bd9Sstevel@tonic-gate 8237c478bd9Sstevel@tonic-gate id = get_int(); 8247c478bd9Sstevel@tonic-gate path = get_string(NULL); 82590685d2cSjp161948 debug3("request %u: opendir", id); 82690685d2cSjp161948 log("opendir \"%s\"", path); 8277c478bd9Sstevel@tonic-gate dirp = opendir(path); 8287c478bd9Sstevel@tonic-gate if (dirp == NULL) { 8297c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 8307c478bd9Sstevel@tonic-gate } else { 83190685d2cSjp161948 handle = handle_new(HANDLE_DIR, path, 0, dirp); 8327c478bd9Sstevel@tonic-gate if (handle < 0) { 8337c478bd9Sstevel@tonic-gate closedir(dirp); 8347c478bd9Sstevel@tonic-gate } else { 8357c478bd9Sstevel@tonic-gate send_handle(id, handle); 8367c478bd9Sstevel@tonic-gate status = SSH2_FX_OK; 8377c478bd9Sstevel@tonic-gate } 8387c478bd9Sstevel@tonic-gate 8397c478bd9Sstevel@tonic-gate } 8407c478bd9Sstevel@tonic-gate if (status != SSH2_FX_OK) 8417c478bd9Sstevel@tonic-gate send_status(id, status); 8427c478bd9Sstevel@tonic-gate xfree(path); 8437c478bd9Sstevel@tonic-gate } 8447c478bd9Sstevel@tonic-gate 8457c478bd9Sstevel@tonic-gate static void 8467c478bd9Sstevel@tonic-gate process_readdir(void) 8477c478bd9Sstevel@tonic-gate { 8487c478bd9Sstevel@tonic-gate DIR *dirp; 8497c478bd9Sstevel@tonic-gate struct dirent *dp; 8507c478bd9Sstevel@tonic-gate char *path; 8517c478bd9Sstevel@tonic-gate int handle; 8527c478bd9Sstevel@tonic-gate u_int32_t id; 8537c478bd9Sstevel@tonic-gate 8547c478bd9Sstevel@tonic-gate id = get_int(); 8557c478bd9Sstevel@tonic-gate handle = get_handle(); 85690685d2cSjp161948 debug("request %u: readdir \"%s\" (handle %d)", id, 85790685d2cSjp161948 handle_to_name(handle), handle); 8587c478bd9Sstevel@tonic-gate dirp = handle_to_dir(handle); 8597c478bd9Sstevel@tonic-gate path = handle_to_name(handle); 8607c478bd9Sstevel@tonic-gate if (dirp == NULL || path == NULL) { 8617c478bd9Sstevel@tonic-gate send_status(id, SSH2_FX_FAILURE); 8627c478bd9Sstevel@tonic-gate } else { 8637c478bd9Sstevel@tonic-gate struct stat st; 86490685d2cSjp161948 char pathname[MAXPATHLEN]; 8657c478bd9Sstevel@tonic-gate Stat *stats; 8667c478bd9Sstevel@tonic-gate int nstats = 10, count = 0, i; 8677c478bd9Sstevel@tonic-gate 86890685d2cSjp161948 stats = xcalloc(nstats, sizeof(Stat)); 8697c478bd9Sstevel@tonic-gate while ((dp = readdir(dirp)) != NULL) { 8707c478bd9Sstevel@tonic-gate if (count >= nstats) { 8717c478bd9Sstevel@tonic-gate nstats *= 2; 8727c478bd9Sstevel@tonic-gate stats = xrealloc(stats, nstats * sizeof(Stat)); 8737c478bd9Sstevel@tonic-gate } 8747c478bd9Sstevel@tonic-gate /* XXX OVERFLOW ? */ 8757c478bd9Sstevel@tonic-gate snprintf(pathname, sizeof pathname, "%s%s%s", path, 8767c478bd9Sstevel@tonic-gate strcmp(path, "/") ? "/" : "", dp->d_name); 8777c478bd9Sstevel@tonic-gate if (lstat(pathname, &st) < 0) 8787c478bd9Sstevel@tonic-gate continue; 8797c478bd9Sstevel@tonic-gate stat_to_attrib(&st, &(stats[count].attrib)); 8807c478bd9Sstevel@tonic-gate stats[count].name = xstrdup(dp->d_name); 8817c478bd9Sstevel@tonic-gate stats[count].long_name = ls_file(dp->d_name, &st, 0); 8827c478bd9Sstevel@tonic-gate count++; 8837c478bd9Sstevel@tonic-gate /* send up to 100 entries in one message */ 8847c478bd9Sstevel@tonic-gate /* XXX check packet size instead */ 8857c478bd9Sstevel@tonic-gate if (count == 100) 8867c478bd9Sstevel@tonic-gate break; 8877c478bd9Sstevel@tonic-gate } 8887c478bd9Sstevel@tonic-gate if (count > 0) { 8897c478bd9Sstevel@tonic-gate send_names(id, count, stats); 8907c478bd9Sstevel@tonic-gate for (i = 0; i < count; i++) { 8917c478bd9Sstevel@tonic-gate xfree(stats[i].name); 8927c478bd9Sstevel@tonic-gate xfree(stats[i].long_name); 8937c478bd9Sstevel@tonic-gate } 8947c478bd9Sstevel@tonic-gate } else { 8957c478bd9Sstevel@tonic-gate send_status(id, SSH2_FX_EOF); 8967c478bd9Sstevel@tonic-gate } 8977c478bd9Sstevel@tonic-gate xfree(stats); 8987c478bd9Sstevel@tonic-gate } 8997c478bd9Sstevel@tonic-gate } 9007c478bd9Sstevel@tonic-gate 9017c478bd9Sstevel@tonic-gate static void 9027c478bd9Sstevel@tonic-gate process_remove(void) 9037c478bd9Sstevel@tonic-gate { 9047c478bd9Sstevel@tonic-gate char *name; 9057c478bd9Sstevel@tonic-gate u_int32_t id; 9067c478bd9Sstevel@tonic-gate int status = SSH2_FX_FAILURE; 9077c478bd9Sstevel@tonic-gate int ret; 9087c478bd9Sstevel@tonic-gate 9097c478bd9Sstevel@tonic-gate id = get_int(); 9107c478bd9Sstevel@tonic-gate name = get_string(NULL); 91190685d2cSjp161948 debug3("request %u: remove", id); 91290685d2cSjp161948 log("remove name \"%s\"", name); 9137c478bd9Sstevel@tonic-gate ret = unlink(name); 9147c478bd9Sstevel@tonic-gate status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 9157c478bd9Sstevel@tonic-gate send_status(id, status); 9167c478bd9Sstevel@tonic-gate xfree(name); 9177c478bd9Sstevel@tonic-gate } 9187c478bd9Sstevel@tonic-gate 9197c478bd9Sstevel@tonic-gate static void 9207c478bd9Sstevel@tonic-gate process_mkdir(void) 9217c478bd9Sstevel@tonic-gate { 9227c478bd9Sstevel@tonic-gate Attrib *a; 9237c478bd9Sstevel@tonic-gate u_int32_t id; 9247c478bd9Sstevel@tonic-gate char *name; 9257c478bd9Sstevel@tonic-gate int ret, mode, status = SSH2_FX_FAILURE; 9267c478bd9Sstevel@tonic-gate 9277c478bd9Sstevel@tonic-gate id = get_int(); 9287c478bd9Sstevel@tonic-gate name = get_string(NULL); 9297c478bd9Sstevel@tonic-gate a = get_attrib(); 9307c478bd9Sstevel@tonic-gate mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 9317c478bd9Sstevel@tonic-gate a->perm & 0777 : 0777; 93290685d2cSjp161948 debug3("request %u: mkdir", id); 93390685d2cSjp161948 log("mkdir name \"%s\" mode 0%o", name, mode); 9347c478bd9Sstevel@tonic-gate ret = mkdir(name, mode); 9357c478bd9Sstevel@tonic-gate status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 9367c478bd9Sstevel@tonic-gate send_status(id, status); 9377c478bd9Sstevel@tonic-gate xfree(name); 9387c478bd9Sstevel@tonic-gate } 9397c478bd9Sstevel@tonic-gate 9407c478bd9Sstevel@tonic-gate static void 9417c478bd9Sstevel@tonic-gate process_rmdir(void) 9427c478bd9Sstevel@tonic-gate { 9437c478bd9Sstevel@tonic-gate u_int32_t id; 9447c478bd9Sstevel@tonic-gate char *name; 9457c478bd9Sstevel@tonic-gate int ret, status; 9467c478bd9Sstevel@tonic-gate 9477c478bd9Sstevel@tonic-gate id = get_int(); 9487c478bd9Sstevel@tonic-gate name = get_string(NULL); 94990685d2cSjp161948 debug3("request %u: rmdir", id); 95090685d2cSjp161948 log("rmdir name \"%s\"", name); 9517c478bd9Sstevel@tonic-gate ret = rmdir(name); 9527c478bd9Sstevel@tonic-gate status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 9537c478bd9Sstevel@tonic-gate send_status(id, status); 9547c478bd9Sstevel@tonic-gate xfree(name); 9557c478bd9Sstevel@tonic-gate } 9567c478bd9Sstevel@tonic-gate 9577c478bd9Sstevel@tonic-gate static void 9587c478bd9Sstevel@tonic-gate process_realpath(void) 9597c478bd9Sstevel@tonic-gate { 9607c478bd9Sstevel@tonic-gate char resolvedname[MAXPATHLEN]; 9617c478bd9Sstevel@tonic-gate u_int32_t id; 9627c478bd9Sstevel@tonic-gate char *path; 9637c478bd9Sstevel@tonic-gate 9647c478bd9Sstevel@tonic-gate id = get_int(); 9657c478bd9Sstevel@tonic-gate path = get_string(NULL); 9667c478bd9Sstevel@tonic-gate if (path[0] == '\0') { 9677c478bd9Sstevel@tonic-gate xfree(path); 9687c478bd9Sstevel@tonic-gate path = xstrdup("."); 9697c478bd9Sstevel@tonic-gate } 97090685d2cSjp161948 debug3("request %u: realpath", id); 97190685d2cSjp161948 verbose("realpath \"%s\"", path); 9727c478bd9Sstevel@tonic-gate if (realpath(path, resolvedname) == NULL) { 9737c478bd9Sstevel@tonic-gate send_status(id, errno_to_portable(errno)); 9747c478bd9Sstevel@tonic-gate } else { 9757c478bd9Sstevel@tonic-gate Stat s; 9767c478bd9Sstevel@tonic-gate attrib_clear(&s.attrib); 9777c478bd9Sstevel@tonic-gate s.name = s.long_name = resolvedname; 9787c478bd9Sstevel@tonic-gate send_names(id, 1, &s); 9797c478bd9Sstevel@tonic-gate } 9807c478bd9Sstevel@tonic-gate xfree(path); 9817c478bd9Sstevel@tonic-gate } 9827c478bd9Sstevel@tonic-gate 9837c478bd9Sstevel@tonic-gate static void 9847c478bd9Sstevel@tonic-gate process_rename(void) 9857c478bd9Sstevel@tonic-gate { 9867c478bd9Sstevel@tonic-gate u_int32_t id; 9877c478bd9Sstevel@tonic-gate char *oldpath, *newpath; 98890685d2cSjp161948 int status; 98990685d2cSjp161948 struct stat sb; 9907c478bd9Sstevel@tonic-gate 9917c478bd9Sstevel@tonic-gate id = get_int(); 9927c478bd9Sstevel@tonic-gate oldpath = get_string(NULL); 9937c478bd9Sstevel@tonic-gate newpath = get_string(NULL); 99490685d2cSjp161948 debug3("request %u: rename", id); 99590685d2cSjp161948 log("rename old \"%s\" new \"%s\"", oldpath, newpath); 99690685d2cSjp161948 status = SSH2_FX_FAILURE; 99790685d2cSjp161948 if (lstat(oldpath, &sb) == -1) 99890685d2cSjp161948 status = errno_to_portable(errno); 99990685d2cSjp161948 else if (S_ISREG(sb.st_mode)) { 100090685d2cSjp161948 /* Race-free rename of regular files */ 100190685d2cSjp161948 if (link(oldpath, newpath) == -1) { 100290685d2cSjp161948 if (errno == EOPNOTSUPP 100390685d2cSjp161948 #ifdef LINK_OPNOTSUPP_ERRNO 100490685d2cSjp161948 || errno == LINK_OPNOTSUPP_ERRNO 100590685d2cSjp161948 #endif 100690685d2cSjp161948 ) { 100790685d2cSjp161948 struct stat st; 100890685d2cSjp161948 100990685d2cSjp161948 /* 101090685d2cSjp161948 * fs doesn't support links, so fall back to 101190685d2cSjp161948 * stat+rename. This is racy. 101290685d2cSjp161948 */ 10137c478bd9Sstevel@tonic-gate if (stat(newpath, &st) == -1) { 101490685d2cSjp161948 if (rename(oldpath, newpath) == -1) 101590685d2cSjp161948 status = 101690685d2cSjp161948 errno_to_portable(errno); 101790685d2cSjp161948 else 101890685d2cSjp161948 status = SSH2_FX_OK; 101990685d2cSjp161948 } 102090685d2cSjp161948 } else { 102190685d2cSjp161948 status = errno_to_portable(errno); 102290685d2cSjp161948 } 102390685d2cSjp161948 } else if (unlink(oldpath) == -1) { 102490685d2cSjp161948 status = errno_to_portable(errno); 102590685d2cSjp161948 /* clean spare link */ 102690685d2cSjp161948 unlink(newpath); 102790685d2cSjp161948 } else 102890685d2cSjp161948 status = SSH2_FX_OK; 102990685d2cSjp161948 } else if (stat(newpath, &sb) == -1) { 103090685d2cSjp161948 if (rename(oldpath, newpath) == -1) 103190685d2cSjp161948 status = errno_to_portable(errno); 103290685d2cSjp161948 else 103390685d2cSjp161948 status = SSH2_FX_OK; 10347c478bd9Sstevel@tonic-gate } 10357c478bd9Sstevel@tonic-gate send_status(id, status); 10367c478bd9Sstevel@tonic-gate xfree(oldpath); 10377c478bd9Sstevel@tonic-gate xfree(newpath); 10387c478bd9Sstevel@tonic-gate } 10397c478bd9Sstevel@tonic-gate 10407c478bd9Sstevel@tonic-gate static void 10417c478bd9Sstevel@tonic-gate process_readlink(void) 10427c478bd9Sstevel@tonic-gate { 10437c478bd9Sstevel@tonic-gate u_int32_t id; 10447c478bd9Sstevel@tonic-gate int len; 104590685d2cSjp161948 char buf[MAXPATHLEN]; 10467c478bd9Sstevel@tonic-gate char *path; 10477c478bd9Sstevel@tonic-gate 10487c478bd9Sstevel@tonic-gate id = get_int(); 10497c478bd9Sstevel@tonic-gate path = get_string(NULL); 105090685d2cSjp161948 debug3("request %u: readlink", id); 105190685d2cSjp161948 verbose("readlink \"%s\"", path); 105290685d2cSjp161948 if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) 10537c478bd9Sstevel@tonic-gate send_status(id, errno_to_portable(errno)); 10547c478bd9Sstevel@tonic-gate else { 10557c478bd9Sstevel@tonic-gate Stat s; 10567c478bd9Sstevel@tonic-gate 105790685d2cSjp161948 buf[len] = '\0'; 10587c478bd9Sstevel@tonic-gate attrib_clear(&s.attrib); 105990685d2cSjp161948 s.name = s.long_name = buf; 10607c478bd9Sstevel@tonic-gate send_names(id, 1, &s); 10617c478bd9Sstevel@tonic-gate } 10627c478bd9Sstevel@tonic-gate xfree(path); 10637c478bd9Sstevel@tonic-gate } 10647c478bd9Sstevel@tonic-gate 10657c478bd9Sstevel@tonic-gate static void 10667c478bd9Sstevel@tonic-gate process_symlink(void) 10677c478bd9Sstevel@tonic-gate { 10687c478bd9Sstevel@tonic-gate u_int32_t id; 10697c478bd9Sstevel@tonic-gate char *oldpath, *newpath; 107090685d2cSjp161948 int ret, status; 10717c478bd9Sstevel@tonic-gate 10727c478bd9Sstevel@tonic-gate id = get_int(); 10737c478bd9Sstevel@tonic-gate oldpath = get_string(NULL); 10747c478bd9Sstevel@tonic-gate newpath = get_string(NULL); 107590685d2cSjp161948 debug3("request %u: symlink", id); 107690685d2cSjp161948 log("symlink old \"%s\" new \"%s\"", oldpath, newpath); 107790685d2cSjp161948 /* this will fail if 'newpath' exists */ 10787c478bd9Sstevel@tonic-gate ret = symlink(oldpath, newpath); 10797c478bd9Sstevel@tonic-gate status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 10807c478bd9Sstevel@tonic-gate send_status(id, status); 10817c478bd9Sstevel@tonic-gate xfree(oldpath); 10827c478bd9Sstevel@tonic-gate xfree(newpath); 10837c478bd9Sstevel@tonic-gate } 10847c478bd9Sstevel@tonic-gate 10857c478bd9Sstevel@tonic-gate static void 10867c478bd9Sstevel@tonic-gate process_extended(void) 10877c478bd9Sstevel@tonic-gate { 10887c478bd9Sstevel@tonic-gate u_int32_t id; 10897c478bd9Sstevel@tonic-gate char *request; 10907c478bd9Sstevel@tonic-gate 10917c478bd9Sstevel@tonic-gate id = get_int(); 10927c478bd9Sstevel@tonic-gate request = get_string(NULL); 10937c478bd9Sstevel@tonic-gate send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 10947c478bd9Sstevel@tonic-gate xfree(request); 10957c478bd9Sstevel@tonic-gate } 10967c478bd9Sstevel@tonic-gate 10977c478bd9Sstevel@tonic-gate /* stolen from ssh-agent */ 10987c478bd9Sstevel@tonic-gate 10997c478bd9Sstevel@tonic-gate static void 11007c478bd9Sstevel@tonic-gate process(void) 11017c478bd9Sstevel@tonic-gate { 11027c478bd9Sstevel@tonic-gate u_int msg_len; 11037c478bd9Sstevel@tonic-gate u_int buf_len; 11047c478bd9Sstevel@tonic-gate u_int consumed; 11057c478bd9Sstevel@tonic-gate u_int type; 11067c478bd9Sstevel@tonic-gate u_char *cp; 11077c478bd9Sstevel@tonic-gate 11087c478bd9Sstevel@tonic-gate buf_len = buffer_len(&iqueue); 11097c478bd9Sstevel@tonic-gate if (buf_len < 5) 11107c478bd9Sstevel@tonic-gate return; /* Incomplete message. */ 11117c478bd9Sstevel@tonic-gate cp = buffer_ptr(&iqueue); 111290685d2cSjp161948 msg_len = get_u32(cp); 111390685d2cSjp161948 if (msg_len > SFTP_MAX_MSG_LENGTH) { 111490685d2cSjp161948 error("bad message from %s local user %s", 111590685d2cSjp161948 client_addr, pw->pw_name); 111690685d2cSjp161948 cleanup_exit(11); 11177c478bd9Sstevel@tonic-gate } 11187c478bd9Sstevel@tonic-gate if (buf_len < msg_len + 4) 11197c478bd9Sstevel@tonic-gate return; 11207c478bd9Sstevel@tonic-gate buffer_consume(&iqueue, 4); 11217c478bd9Sstevel@tonic-gate buf_len -= 4; 11227c478bd9Sstevel@tonic-gate type = buffer_get_char(&iqueue); 11237c478bd9Sstevel@tonic-gate switch (type) { 11247c478bd9Sstevel@tonic-gate case SSH2_FXP_INIT: 11257c478bd9Sstevel@tonic-gate process_init(); 11267c478bd9Sstevel@tonic-gate break; 11277c478bd9Sstevel@tonic-gate case SSH2_FXP_OPEN: 11287c478bd9Sstevel@tonic-gate process_open(); 11297c478bd9Sstevel@tonic-gate break; 11307c478bd9Sstevel@tonic-gate case SSH2_FXP_CLOSE: 11317c478bd9Sstevel@tonic-gate process_close(); 11327c478bd9Sstevel@tonic-gate break; 11337c478bd9Sstevel@tonic-gate case SSH2_FXP_READ: 11347c478bd9Sstevel@tonic-gate process_read(); 11357c478bd9Sstevel@tonic-gate break; 11367c478bd9Sstevel@tonic-gate case SSH2_FXP_WRITE: 11377c478bd9Sstevel@tonic-gate process_write(); 11387c478bd9Sstevel@tonic-gate break; 11397c478bd9Sstevel@tonic-gate case SSH2_FXP_LSTAT: 11407c478bd9Sstevel@tonic-gate process_lstat(); 11417c478bd9Sstevel@tonic-gate break; 11427c478bd9Sstevel@tonic-gate case SSH2_FXP_FSTAT: 11437c478bd9Sstevel@tonic-gate process_fstat(); 11447c478bd9Sstevel@tonic-gate break; 11457c478bd9Sstevel@tonic-gate case SSH2_FXP_SETSTAT: 11467c478bd9Sstevel@tonic-gate process_setstat(); 11477c478bd9Sstevel@tonic-gate break; 11487c478bd9Sstevel@tonic-gate case SSH2_FXP_FSETSTAT: 11497c478bd9Sstevel@tonic-gate process_fsetstat(); 11507c478bd9Sstevel@tonic-gate break; 11517c478bd9Sstevel@tonic-gate case SSH2_FXP_OPENDIR: 11527c478bd9Sstevel@tonic-gate process_opendir(); 11537c478bd9Sstevel@tonic-gate break; 11547c478bd9Sstevel@tonic-gate case SSH2_FXP_READDIR: 11557c478bd9Sstevel@tonic-gate process_readdir(); 11567c478bd9Sstevel@tonic-gate break; 11577c478bd9Sstevel@tonic-gate case SSH2_FXP_REMOVE: 11587c478bd9Sstevel@tonic-gate process_remove(); 11597c478bd9Sstevel@tonic-gate break; 11607c478bd9Sstevel@tonic-gate case SSH2_FXP_MKDIR: 11617c478bd9Sstevel@tonic-gate process_mkdir(); 11627c478bd9Sstevel@tonic-gate break; 11637c478bd9Sstevel@tonic-gate case SSH2_FXP_RMDIR: 11647c478bd9Sstevel@tonic-gate process_rmdir(); 11657c478bd9Sstevel@tonic-gate break; 11667c478bd9Sstevel@tonic-gate case SSH2_FXP_REALPATH: 11677c478bd9Sstevel@tonic-gate process_realpath(); 11687c478bd9Sstevel@tonic-gate break; 11697c478bd9Sstevel@tonic-gate case SSH2_FXP_STAT: 11707c478bd9Sstevel@tonic-gate process_stat(); 11717c478bd9Sstevel@tonic-gate break; 11727c478bd9Sstevel@tonic-gate case SSH2_FXP_RENAME: 11737c478bd9Sstevel@tonic-gate process_rename(); 11747c478bd9Sstevel@tonic-gate break; 11757c478bd9Sstevel@tonic-gate case SSH2_FXP_READLINK: 11767c478bd9Sstevel@tonic-gate process_readlink(); 11777c478bd9Sstevel@tonic-gate break; 11787c478bd9Sstevel@tonic-gate case SSH2_FXP_SYMLINK: 11797c478bd9Sstevel@tonic-gate process_symlink(); 11807c478bd9Sstevel@tonic-gate break; 11817c478bd9Sstevel@tonic-gate case SSH2_FXP_EXTENDED: 11827c478bd9Sstevel@tonic-gate process_extended(); 11837c478bd9Sstevel@tonic-gate break; 11847c478bd9Sstevel@tonic-gate default: 11857c478bd9Sstevel@tonic-gate error("Unknown message %d", type); 11867c478bd9Sstevel@tonic-gate break; 11877c478bd9Sstevel@tonic-gate } 11887c478bd9Sstevel@tonic-gate /* discard the remaining bytes from the current packet */ 11897c478bd9Sstevel@tonic-gate if (buf_len < buffer_len(&iqueue)) 119090685d2cSjp161948 fatal("iqueue grew unexpectedly"); 11917c478bd9Sstevel@tonic-gate consumed = buf_len - buffer_len(&iqueue); 11927c478bd9Sstevel@tonic-gate if (msg_len < consumed) 11937c478bd9Sstevel@tonic-gate fatal("msg_len %d < consumed %d", msg_len, consumed); 11947c478bd9Sstevel@tonic-gate if (msg_len > consumed) 11957c478bd9Sstevel@tonic-gate buffer_consume(&iqueue, msg_len - consumed); 11967c478bd9Sstevel@tonic-gate } 11977c478bd9Sstevel@tonic-gate 119890685d2cSjp161948 /* Cleanup handler that logs active handles upon normal exit */ 119990685d2cSjp161948 static void 120090685d2cSjp161948 cleanup_exit(int i) 120190685d2cSjp161948 { 120290685d2cSjp161948 if (pw != NULL && client_addr != NULL) { 120390685d2cSjp161948 handle_log_exit(); 120490685d2cSjp161948 log("session closed for local user %s from [%s]", 120590685d2cSjp161948 pw->pw_name, client_addr); 120690685d2cSjp161948 } 120790685d2cSjp161948 _exit(i); 120890685d2cSjp161948 } 120990685d2cSjp161948 121090685d2cSjp161948 static void 121190685d2cSjp161948 usage(void) 121290685d2cSjp161948 { 121390685d2cSjp161948 fprintf(stderr, 121490685d2cSjp161948 "Usage: %s [-he] [-l log_level] [-f log_facility]\n", __progname); 121590685d2cSjp161948 exit(1); 121690685d2cSjp161948 } 121790685d2cSjp161948 12187c478bd9Sstevel@tonic-gate int 121990685d2cSjp161948 main(int argc, char **argv) 12207c478bd9Sstevel@tonic-gate { 12217c478bd9Sstevel@tonic-gate fd_set *rset, *wset; 122290685d2cSjp161948 int in, out, max, ch, skipargs = 0, log_stderr = 0; 12237c478bd9Sstevel@tonic-gate ssize_t len, olen, set_size; 122490685d2cSjp161948 SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; 122590685d2cSjp161948 char *cp, buf[4*4096]; 12267c478bd9Sstevel@tonic-gate 122790685d2cSjp161948 extern char *optarg; 12287c478bd9Sstevel@tonic-gate 122990685d2cSjp161948 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 123090685d2cSjp161948 sanitise_stdfd(); 123190685d2cSjp161948 123290685d2cSjp161948 __progname = get_progname(argv[0]); 12337c478bd9Sstevel@tonic-gate 12347c478bd9Sstevel@tonic-gate (void) g11n_setlocale(LC_ALL, ""); 12357c478bd9Sstevel@tonic-gate 123690685d2cSjp161948 log_init(__progname, log_level, log_facility, log_stderr); 12377c478bd9Sstevel@tonic-gate 123890685d2cSjp161948 while (!skipargs && (ch = getopt(argc, argv, "C:f:l:che")) != -1) { 123990685d2cSjp161948 switch (ch) { 124090685d2cSjp161948 case 'c': 124190685d2cSjp161948 /* 124290685d2cSjp161948 * Ignore all arguments if we are invoked as a 124390685d2cSjp161948 * shell using "sftp-server -c command" 124490685d2cSjp161948 */ 124590685d2cSjp161948 skipargs = 1; 124690685d2cSjp161948 break; 124790685d2cSjp161948 case 'e': 124890685d2cSjp161948 log_stderr = 1; 124990685d2cSjp161948 break; 125090685d2cSjp161948 case 'l': 125190685d2cSjp161948 log_level = log_level_number(optarg); 125290685d2cSjp161948 if (log_level == SYSLOG_LEVEL_NOT_SET) 125390685d2cSjp161948 error("Invalid log level \"%s\"", optarg); 125490685d2cSjp161948 break; 125590685d2cSjp161948 case 'f': 125690685d2cSjp161948 log_facility = log_facility_number(optarg); 125790685d2cSjp161948 if (log_facility == SYSLOG_FACILITY_NOT_SET) 125890685d2cSjp161948 error("Invalid log facility \"%s\"", optarg); 125990685d2cSjp161948 break; 126090685d2cSjp161948 case 'h': 126190685d2cSjp161948 default: 126290685d2cSjp161948 usage(); 126390685d2cSjp161948 } 126490685d2cSjp161948 } 126590685d2cSjp161948 126690685d2cSjp161948 log_init(__progname, log_level, log_facility, log_stderr); 126790685d2cSjp161948 126890685d2cSjp161948 if ((cp = getenv("SSH_CONNECTION")) != NULL) { 126990685d2cSjp161948 client_addr = xstrdup(cp); 127090685d2cSjp161948 if ((cp = strchr(client_addr, ' ')) == NULL) 127190685d2cSjp161948 fatal("Malformed SSH_CONNECTION variable: \"%s\"", 127290685d2cSjp161948 getenv("SSH_CONNECTION")); 127390685d2cSjp161948 *cp = '\0'; 127490685d2cSjp161948 } else 127590685d2cSjp161948 client_addr = xstrdup("UNKNOWN"); 127690685d2cSjp161948 127790685d2cSjp161948 if ((pw = getpwuid(getuid())) == NULL) 127890685d2cSjp161948 fatal("No user found for uid %lu", (u_long)getuid()); 127990685d2cSjp161948 pw = pwcopy(pw); 128090685d2cSjp161948 128190685d2cSjp161948 log("session opened for local user %s from [%s]", 128290685d2cSjp161948 pw->pw_name, client_addr); 128390685d2cSjp161948 128490685d2cSjp161948 handle_init(); 12857c478bd9Sstevel@tonic-gate 12867c478bd9Sstevel@tonic-gate in = dup(STDIN_FILENO); 12877c478bd9Sstevel@tonic-gate out = dup(STDOUT_FILENO); 12887c478bd9Sstevel@tonic-gate 12897c478bd9Sstevel@tonic-gate #ifdef HAVE_CYGWIN 12907c478bd9Sstevel@tonic-gate setmode(in, O_BINARY); 12917c478bd9Sstevel@tonic-gate setmode(out, O_BINARY); 12927c478bd9Sstevel@tonic-gate #endif 12937c478bd9Sstevel@tonic-gate 12947c478bd9Sstevel@tonic-gate max = 0; 12957c478bd9Sstevel@tonic-gate if (in > max) 12967c478bd9Sstevel@tonic-gate max = in; 12977c478bd9Sstevel@tonic-gate if (out > max) 12987c478bd9Sstevel@tonic-gate max = out; 12997c478bd9Sstevel@tonic-gate 13007c478bd9Sstevel@tonic-gate buffer_init(&iqueue); 13017c478bd9Sstevel@tonic-gate buffer_init(&oqueue); 13027c478bd9Sstevel@tonic-gate 13037c478bd9Sstevel@tonic-gate set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 13047c478bd9Sstevel@tonic-gate rset = (fd_set *)xmalloc(set_size); 13057c478bd9Sstevel@tonic-gate wset = (fd_set *)xmalloc(set_size); 13067c478bd9Sstevel@tonic-gate 13077c478bd9Sstevel@tonic-gate for (;;) { 13087c478bd9Sstevel@tonic-gate memset(rset, 0, set_size); 13097c478bd9Sstevel@tonic-gate memset(wset, 0, set_size); 13107c478bd9Sstevel@tonic-gate 131190685d2cSjp161948 /* 131290685d2cSjp161948 * Ensure that we can read a full buffer and handle 131390685d2cSjp161948 * the worst-case length packet it can generate, 131490685d2cSjp161948 * otherwise apply backpressure by stopping reads. 131590685d2cSjp161948 */ 131690685d2cSjp161948 if (buffer_check_alloc(&iqueue, sizeof(buf)) && 131790685d2cSjp161948 buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) 13187c478bd9Sstevel@tonic-gate FD_SET(in, rset); 131990685d2cSjp161948 13207c478bd9Sstevel@tonic-gate olen = buffer_len(&oqueue); 13217c478bd9Sstevel@tonic-gate if (olen > 0) 13227c478bd9Sstevel@tonic-gate FD_SET(out, wset); 13237c478bd9Sstevel@tonic-gate 13247c478bd9Sstevel@tonic-gate if (select(max+1, rset, wset, NULL, NULL) < 0) { 13257c478bd9Sstevel@tonic-gate if (errno == EINTR) 13267c478bd9Sstevel@tonic-gate continue; 132790685d2cSjp161948 error("select: %s", strerror(errno)); 132890685d2cSjp161948 cleanup_exit(2); 13297c478bd9Sstevel@tonic-gate } 13307c478bd9Sstevel@tonic-gate 13317c478bd9Sstevel@tonic-gate /* copy stdin to iqueue */ 13327c478bd9Sstevel@tonic-gate if (FD_ISSET(in, rset)) { 13337c478bd9Sstevel@tonic-gate len = read(in, buf, sizeof buf); 13347c478bd9Sstevel@tonic-gate if (len == 0) { 13357c478bd9Sstevel@tonic-gate debug("read eof"); 133690685d2cSjp161948 cleanup_exit(0); 13377c478bd9Sstevel@tonic-gate } else if (len < 0) { 133890685d2cSjp161948 error("read: %s", strerror(errno)); 133990685d2cSjp161948 cleanup_exit(1); 13407c478bd9Sstevel@tonic-gate } else { 13417c478bd9Sstevel@tonic-gate buffer_append(&iqueue, buf, len); 13427c478bd9Sstevel@tonic-gate } 13437c478bd9Sstevel@tonic-gate } 13447c478bd9Sstevel@tonic-gate /* send oqueue to stdout */ 13457c478bd9Sstevel@tonic-gate if (FD_ISSET(out, wset)) { 13467c478bd9Sstevel@tonic-gate len = write(out, buffer_ptr(&oqueue), olen); 13477c478bd9Sstevel@tonic-gate if (len < 0) { 134890685d2cSjp161948 error("write: %s", strerror(errno)); 134990685d2cSjp161948 cleanup_exit(1); 13507c478bd9Sstevel@tonic-gate } else { 13517c478bd9Sstevel@tonic-gate buffer_consume(&oqueue, len); 13527c478bd9Sstevel@tonic-gate } 13537c478bd9Sstevel@tonic-gate } 135490685d2cSjp161948 135590685d2cSjp161948 /* 135690685d2cSjp161948 * Process requests from client if we can fit the results 135790685d2cSjp161948 * into the output buffer, otherwise stop processing input 135890685d2cSjp161948 * and let the output queue drain. 135990685d2cSjp161948 */ 136090685d2cSjp161948 if (buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) 13617c478bd9Sstevel@tonic-gate process(); 13627c478bd9Sstevel@tonic-gate } 136390685d2cSjp161948 136490685d2cSjp161948 /* NOTREACHED */ 136590685d2cSjp161948 return (0); 13667c478bd9Sstevel@tonic-gate } 1367