17c478bd9Sstevel@tonic-gate /* 2*90685d2cSjp161948 * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. 3*90685d2cSjp161948 * 4*90685d2cSjp161948 * Permission to use, copy, modify, and distribute this software for any 5*90685d2cSjp161948 * purpose with or without fee is hereby granted, provided that the above 6*90685d2cSjp161948 * copyright notice and this permission notice appear in all copies. 7*90685d2cSjp161948 * 8*90685d2cSjp161948 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9*90685d2cSjp161948 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10*90685d2cSjp161948 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11*90685d2cSjp161948 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12*90685d2cSjp161948 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13*90685d2cSjp161948 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14*90685d2cSjp161948 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 157c478bd9Sstevel@tonic-gate */ 167c478bd9Sstevel@tonic-gate 17*90685d2cSjp161948 /* $OpenBSD: sftp-server.c,v 1.71 2007/01/03 07:22:36 stevesk Exp $ */ 187c478bd9Sstevel@tonic-gate 197c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 207c478bd9Sstevel@tonic-gate 21*90685d2cSjp161948 #include "includes.h" 22*90685d2cSjp161948 23*90685d2cSjp161948 #include <sys/types.h> 24*90685d2cSjp161948 #include <sys/param.h> 25*90685d2cSjp161948 #include <sys/stat.h> 26*90685d2cSjp161948 #ifdef HAVE_SYS_TIME_H 27*90685d2cSjp161948 # include <sys/time.h> 28*90685d2cSjp161948 #endif 29*90685d2cSjp161948 30*90685d2cSjp161948 #include <dirent.h> 31*90685d2cSjp161948 #include <errno.h> 32*90685d2cSjp161948 #include <fcntl.h> 33*90685d2cSjp161948 #include <pwd.h> 34*90685d2cSjp161948 #include <stdlib.h> 35*90685d2cSjp161948 #include <stdio.h> 36*90685d2cSjp161948 #include <string.h> 37*90685d2cSjp161948 #include <pwd.h> 38*90685d2cSjp161948 #include <time.h> 39*90685d2cSjp161948 #include <unistd.h> 40*90685d2cSjp161948 #include <stdarg.h> 41*90685d2cSjp161948 42*90685d2cSjp161948 #include "xmalloc.h" 437c478bd9Sstevel@tonic-gate #include "buffer.h" 447c478bd9Sstevel@tonic-gate #include "bufaux.h" 457c478bd9Sstevel@tonic-gate #include "log.h" 46*90685d2cSjp161948 #include "misc.h" 47*90685d2cSjp161948 #include "uidswap.h" 487c478bd9Sstevel@tonic-gate 497c478bd9Sstevel@tonic-gate #include "sftp.h" 507c478bd9Sstevel@tonic-gate #include "sftp-common.h" 517c478bd9Sstevel@tonic-gate 527c478bd9Sstevel@tonic-gate #ifdef HAVE___PROGNAME 537c478bd9Sstevel@tonic-gate extern char *__progname; 547c478bd9Sstevel@tonic-gate #else 557c478bd9Sstevel@tonic-gate char *__progname; 567c478bd9Sstevel@tonic-gate #endif 577c478bd9Sstevel@tonic-gate 58*90685d2cSjp161948 /* helper */ 59*90685d2cSjp161948 #define get_int64() buffer_get_int64(&iqueue); 60*90685d2cSjp161948 #define get_int() buffer_get_int(&iqueue); 61*90685d2cSjp161948 #define get_string(lenp) buffer_get_string(&iqueue, lenp); 62*90685d2cSjp161948 63*90685d2cSjp161948 static void cleanup_exit(int i); 64*90685d2cSjp161948 65*90685d2cSjp161948 /* Our verbosity */ 66*90685d2cSjp161948 LogLevel log_level = SYSLOG_LEVEL_ERROR; 67*90685d2cSjp161948 68*90685d2cSjp161948 /* Our client */ 69*90685d2cSjp161948 struct passwd *pw = NULL; 70*90685d2cSjp161948 char *client_addr = NULL; 71*90685d2cSjp161948 727c478bd9Sstevel@tonic-gate /* input and output queue */ 737c478bd9Sstevel@tonic-gate Buffer iqueue; 747c478bd9Sstevel@tonic-gate Buffer oqueue; 757c478bd9Sstevel@tonic-gate 767c478bd9Sstevel@tonic-gate /* Version of client */ 777c478bd9Sstevel@tonic-gate int version; 787c478bd9Sstevel@tonic-gate 79*90685d2cSjp161948 /* portable attributes, etc. */ 807c478bd9Sstevel@tonic-gate 817c478bd9Sstevel@tonic-gate typedef struct Stat Stat; 827c478bd9Sstevel@tonic-gate 837c478bd9Sstevel@tonic-gate struct Stat { 847c478bd9Sstevel@tonic-gate char *name; 857c478bd9Sstevel@tonic-gate char *long_name; 867c478bd9Sstevel@tonic-gate Attrib attrib; 877c478bd9Sstevel@tonic-gate }; 887c478bd9Sstevel@tonic-gate 897c478bd9Sstevel@tonic-gate static int 907c478bd9Sstevel@tonic-gate errno_to_portable(int unixerrno) 917c478bd9Sstevel@tonic-gate { 927c478bd9Sstevel@tonic-gate int ret = 0; 937c478bd9Sstevel@tonic-gate 947c478bd9Sstevel@tonic-gate switch (unixerrno) { 957c478bd9Sstevel@tonic-gate case 0: 967c478bd9Sstevel@tonic-gate ret = SSH2_FX_OK; 977c478bd9Sstevel@tonic-gate break; 987c478bd9Sstevel@tonic-gate case ENOENT: 997c478bd9Sstevel@tonic-gate case ENOTDIR: 1007c478bd9Sstevel@tonic-gate case EBADF: 1017c478bd9Sstevel@tonic-gate case ELOOP: 1027c478bd9Sstevel@tonic-gate ret = SSH2_FX_NO_SUCH_FILE; 1037c478bd9Sstevel@tonic-gate break; 1047c478bd9Sstevel@tonic-gate case EPERM: 1057c478bd9Sstevel@tonic-gate case EACCES: 1067c478bd9Sstevel@tonic-gate case EFAULT: 1077c478bd9Sstevel@tonic-gate ret = SSH2_FX_PERMISSION_DENIED; 1087c478bd9Sstevel@tonic-gate break; 1097c478bd9Sstevel@tonic-gate case ENAMETOOLONG: 1107c478bd9Sstevel@tonic-gate case EINVAL: 1117c478bd9Sstevel@tonic-gate ret = SSH2_FX_BAD_MESSAGE; 1127c478bd9Sstevel@tonic-gate break; 1137c478bd9Sstevel@tonic-gate default: 1147c478bd9Sstevel@tonic-gate ret = SSH2_FX_FAILURE; 1157c478bd9Sstevel@tonic-gate break; 1167c478bd9Sstevel@tonic-gate } 1177c478bd9Sstevel@tonic-gate return ret; 1187c478bd9Sstevel@tonic-gate } 1197c478bd9Sstevel@tonic-gate 1207c478bd9Sstevel@tonic-gate static int 1217c478bd9Sstevel@tonic-gate flags_from_portable(int pflags) 1227c478bd9Sstevel@tonic-gate { 1237c478bd9Sstevel@tonic-gate int flags = 0; 1247c478bd9Sstevel@tonic-gate 1257c478bd9Sstevel@tonic-gate if ((pflags & SSH2_FXF_READ) && 1267c478bd9Sstevel@tonic-gate (pflags & SSH2_FXF_WRITE)) { 1277c478bd9Sstevel@tonic-gate flags = O_RDWR; 1287c478bd9Sstevel@tonic-gate } else if (pflags & SSH2_FXF_READ) { 1297c478bd9Sstevel@tonic-gate flags = O_RDONLY; 1307c478bd9Sstevel@tonic-gate } else if (pflags & SSH2_FXF_WRITE) { 1317c478bd9Sstevel@tonic-gate flags = O_WRONLY; 1327c478bd9Sstevel@tonic-gate } 1337c478bd9Sstevel@tonic-gate if (pflags & SSH2_FXF_CREAT) 1347c478bd9Sstevel@tonic-gate flags |= O_CREAT; 1357c478bd9Sstevel@tonic-gate if (pflags & SSH2_FXF_TRUNC) 1367c478bd9Sstevel@tonic-gate flags |= O_TRUNC; 1377c478bd9Sstevel@tonic-gate if (pflags & SSH2_FXF_EXCL) 1387c478bd9Sstevel@tonic-gate flags |= O_EXCL; 1397c478bd9Sstevel@tonic-gate return flags; 1407c478bd9Sstevel@tonic-gate } 1417c478bd9Sstevel@tonic-gate 142*90685d2cSjp161948 static const char * 143*90685d2cSjp161948 string_from_portable(int pflags) 144*90685d2cSjp161948 { 145*90685d2cSjp161948 static char ret[128]; 146*90685d2cSjp161948 147*90685d2cSjp161948 *ret = '\0'; 148*90685d2cSjp161948 149*90685d2cSjp161948 #define PAPPEND(str) { \ 150*90685d2cSjp161948 if (*ret != '\0') \ 151*90685d2cSjp161948 strlcat(ret, ",", sizeof(ret)); \ 152*90685d2cSjp161948 strlcat(ret, str, sizeof(ret)); \ 153*90685d2cSjp161948 } 154*90685d2cSjp161948 155*90685d2cSjp161948 if (pflags & SSH2_FXF_READ) 156*90685d2cSjp161948 PAPPEND("READ") 157*90685d2cSjp161948 if (pflags & SSH2_FXF_WRITE) 158*90685d2cSjp161948 PAPPEND("WRITE") 159*90685d2cSjp161948 if (pflags & SSH2_FXF_CREAT) 160*90685d2cSjp161948 PAPPEND("CREATE") 161*90685d2cSjp161948 if (pflags & SSH2_FXF_TRUNC) 162*90685d2cSjp161948 PAPPEND("TRUNCATE") 163*90685d2cSjp161948 if (pflags & SSH2_FXF_EXCL) 164*90685d2cSjp161948 PAPPEND("EXCL") 165*90685d2cSjp161948 166*90685d2cSjp161948 return ret; 167*90685d2cSjp161948 } 168*90685d2cSjp161948 1697c478bd9Sstevel@tonic-gate static Attrib * 1707c478bd9Sstevel@tonic-gate get_attrib(void) 1717c478bd9Sstevel@tonic-gate { 1727c478bd9Sstevel@tonic-gate return decode_attrib(&iqueue); 1737c478bd9Sstevel@tonic-gate } 1747c478bd9Sstevel@tonic-gate 1757c478bd9Sstevel@tonic-gate /* handle handles */ 1767c478bd9Sstevel@tonic-gate 1777c478bd9Sstevel@tonic-gate typedef struct Handle Handle; 1787c478bd9Sstevel@tonic-gate struct Handle { 1797c478bd9Sstevel@tonic-gate int use; 1807c478bd9Sstevel@tonic-gate DIR *dirp; 1817c478bd9Sstevel@tonic-gate int fd; 1827c478bd9Sstevel@tonic-gate char *name; 183*90685d2cSjp161948 u_int64_t bytes_read, bytes_write; 1847c478bd9Sstevel@tonic-gate }; 1857c478bd9Sstevel@tonic-gate 1867c478bd9Sstevel@tonic-gate enum { 1877c478bd9Sstevel@tonic-gate HANDLE_UNUSED, 1887c478bd9Sstevel@tonic-gate HANDLE_DIR, 1897c478bd9Sstevel@tonic-gate HANDLE_FILE 1907c478bd9Sstevel@tonic-gate }; 1917c478bd9Sstevel@tonic-gate 1927c478bd9Sstevel@tonic-gate Handle handles[100]; 1937c478bd9Sstevel@tonic-gate 1947c478bd9Sstevel@tonic-gate static void 1957c478bd9Sstevel@tonic-gate handle_init(void) 1967c478bd9Sstevel@tonic-gate { 197*90685d2cSjp161948 u_int i; 1987c478bd9Sstevel@tonic-gate 1997c478bd9Sstevel@tonic-gate for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) 2007c478bd9Sstevel@tonic-gate handles[i].use = HANDLE_UNUSED; 2017c478bd9Sstevel@tonic-gate } 2027c478bd9Sstevel@tonic-gate 2037c478bd9Sstevel@tonic-gate static int 204*90685d2cSjp161948 handle_new(int use, const char *name, int fd, DIR *dirp) 2057c478bd9Sstevel@tonic-gate { 206*90685d2cSjp161948 u_int i; 2077c478bd9Sstevel@tonic-gate 2087c478bd9Sstevel@tonic-gate for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) { 2097c478bd9Sstevel@tonic-gate if (handles[i].use == HANDLE_UNUSED) { 2107c478bd9Sstevel@tonic-gate handles[i].use = use; 2117c478bd9Sstevel@tonic-gate handles[i].dirp = dirp; 2127c478bd9Sstevel@tonic-gate handles[i].fd = fd; 213*90685d2cSjp161948 handles[i].name = xstrdup(name); 214*90685d2cSjp161948 handles[i].bytes_read = handles[i].bytes_write = 0; 2157c478bd9Sstevel@tonic-gate return i; 2167c478bd9Sstevel@tonic-gate } 2177c478bd9Sstevel@tonic-gate } 2187c478bd9Sstevel@tonic-gate return -1; 2197c478bd9Sstevel@tonic-gate } 2207c478bd9Sstevel@tonic-gate 2217c478bd9Sstevel@tonic-gate static int 2227c478bd9Sstevel@tonic-gate handle_is_ok(int i, int type) 2237c478bd9Sstevel@tonic-gate { 224*90685d2cSjp161948 return i >= 0 && (u_int)i < sizeof(handles)/sizeof(Handle) && 2257c478bd9Sstevel@tonic-gate handles[i].use == type; 2267c478bd9Sstevel@tonic-gate } 2277c478bd9Sstevel@tonic-gate 2287c478bd9Sstevel@tonic-gate static int 2297c478bd9Sstevel@tonic-gate handle_to_string(int handle, char **stringp, int *hlenp) 2307c478bd9Sstevel@tonic-gate { 2317c478bd9Sstevel@tonic-gate if (stringp == NULL || hlenp == NULL) 2327c478bd9Sstevel@tonic-gate return -1; 2337c478bd9Sstevel@tonic-gate *stringp = xmalloc(sizeof(int32_t)); 234*90685d2cSjp161948 put_u32(*stringp, handle); 2357c478bd9Sstevel@tonic-gate *hlenp = sizeof(int32_t); 2367c478bd9Sstevel@tonic-gate return 0; 2377c478bd9Sstevel@tonic-gate } 2387c478bd9Sstevel@tonic-gate 2397c478bd9Sstevel@tonic-gate static int 240*90685d2cSjp161948 handle_from_string(const char *handle, u_int hlen) 2417c478bd9Sstevel@tonic-gate { 2427c478bd9Sstevel@tonic-gate int val; 2437c478bd9Sstevel@tonic-gate 2447c478bd9Sstevel@tonic-gate if (hlen != sizeof(int32_t)) 2457c478bd9Sstevel@tonic-gate return -1; 246*90685d2cSjp161948 val = get_u32(handle); 2477c478bd9Sstevel@tonic-gate if (handle_is_ok(val, HANDLE_FILE) || 2487c478bd9Sstevel@tonic-gate handle_is_ok(val, HANDLE_DIR)) 2497c478bd9Sstevel@tonic-gate return val; 2507c478bd9Sstevel@tonic-gate return -1; 2517c478bd9Sstevel@tonic-gate } 2527c478bd9Sstevel@tonic-gate 2537c478bd9Sstevel@tonic-gate static char * 2547c478bd9Sstevel@tonic-gate handle_to_name(int handle) 2557c478bd9Sstevel@tonic-gate { 2567c478bd9Sstevel@tonic-gate if (handle_is_ok(handle, HANDLE_DIR)|| 2577c478bd9Sstevel@tonic-gate handle_is_ok(handle, HANDLE_FILE)) 2587c478bd9Sstevel@tonic-gate return handles[handle].name; 2597c478bd9Sstevel@tonic-gate return NULL; 2607c478bd9Sstevel@tonic-gate } 2617c478bd9Sstevel@tonic-gate 2627c478bd9Sstevel@tonic-gate static DIR * 2637c478bd9Sstevel@tonic-gate handle_to_dir(int handle) 2647c478bd9Sstevel@tonic-gate { 2657c478bd9Sstevel@tonic-gate if (handle_is_ok(handle, HANDLE_DIR)) 2667c478bd9Sstevel@tonic-gate return handles[handle].dirp; 2677c478bd9Sstevel@tonic-gate return NULL; 2687c478bd9Sstevel@tonic-gate } 2697c478bd9Sstevel@tonic-gate 2707c478bd9Sstevel@tonic-gate static int 2717c478bd9Sstevel@tonic-gate handle_to_fd(int handle) 2727c478bd9Sstevel@tonic-gate { 2737c478bd9Sstevel@tonic-gate if (handle_is_ok(handle, HANDLE_FILE)) 2747c478bd9Sstevel@tonic-gate return handles[handle].fd; 2757c478bd9Sstevel@tonic-gate return -1; 2767c478bd9Sstevel@tonic-gate } 2777c478bd9Sstevel@tonic-gate 278*90685d2cSjp161948 static void 279*90685d2cSjp161948 handle_update_read(int handle, ssize_t bytes) 280*90685d2cSjp161948 { 281*90685d2cSjp161948 if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 282*90685d2cSjp161948 handles[handle].bytes_read += bytes; 283*90685d2cSjp161948 } 284*90685d2cSjp161948 285*90685d2cSjp161948 static void 286*90685d2cSjp161948 handle_update_write(int handle, ssize_t bytes) 287*90685d2cSjp161948 { 288*90685d2cSjp161948 if (handle_is_ok(handle, HANDLE_FILE) && bytes > 0) 289*90685d2cSjp161948 handles[handle].bytes_write += bytes; 290*90685d2cSjp161948 } 291*90685d2cSjp161948 292*90685d2cSjp161948 static u_int64_t 293*90685d2cSjp161948 handle_bytes_read(int handle) 294*90685d2cSjp161948 { 295*90685d2cSjp161948 if (handle_is_ok(handle, HANDLE_FILE)) 296*90685d2cSjp161948 return (handles[handle].bytes_read); 297*90685d2cSjp161948 return 0; 298*90685d2cSjp161948 } 299*90685d2cSjp161948 300*90685d2cSjp161948 static u_int64_t 301*90685d2cSjp161948 handle_bytes_write(int handle) 302*90685d2cSjp161948 { 303*90685d2cSjp161948 if (handle_is_ok(handle, HANDLE_FILE)) 304*90685d2cSjp161948 return (handles[handle].bytes_write); 305*90685d2cSjp161948 return 0; 306*90685d2cSjp161948 } 307*90685d2cSjp161948 3087c478bd9Sstevel@tonic-gate static int 3097c478bd9Sstevel@tonic-gate handle_close(int handle) 3107c478bd9Sstevel@tonic-gate { 3117c478bd9Sstevel@tonic-gate int ret = -1; 3127c478bd9Sstevel@tonic-gate 3137c478bd9Sstevel@tonic-gate if (handle_is_ok(handle, HANDLE_FILE)) { 3147c478bd9Sstevel@tonic-gate ret = close(handles[handle].fd); 3157c478bd9Sstevel@tonic-gate handles[handle].use = HANDLE_UNUSED; 316*90685d2cSjp161948 xfree(handles[handle].name); 3177c478bd9Sstevel@tonic-gate } else if (handle_is_ok(handle, HANDLE_DIR)) { 3187c478bd9Sstevel@tonic-gate ret = closedir(handles[handle].dirp); 3197c478bd9Sstevel@tonic-gate handles[handle].use = HANDLE_UNUSED; 320*90685d2cSjp161948 xfree(handles[handle].name); 3217c478bd9Sstevel@tonic-gate } else { 3227c478bd9Sstevel@tonic-gate errno = ENOENT; 3237c478bd9Sstevel@tonic-gate } 3247c478bd9Sstevel@tonic-gate return ret; 3257c478bd9Sstevel@tonic-gate } 3267c478bd9Sstevel@tonic-gate 327*90685d2cSjp161948 static void 328*90685d2cSjp161948 handle_log_close(int handle, char *emsg) 329*90685d2cSjp161948 { 330*90685d2cSjp161948 if (handle_is_ok(handle, HANDLE_FILE)) { 331*90685d2cSjp161948 log("%s%sclose \"%s\" bytes read %llu written %llu", 332*90685d2cSjp161948 emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 333*90685d2cSjp161948 handle_to_name(handle), 334*90685d2cSjp161948 (unsigned long long)handle_bytes_read(handle), 335*90685d2cSjp161948 (unsigned long long)handle_bytes_write(handle)); 336*90685d2cSjp161948 } else { 337*90685d2cSjp161948 log("%s%sclosedir \"%s\"", 338*90685d2cSjp161948 emsg == NULL ? "" : emsg, emsg == NULL ? "" : " ", 339*90685d2cSjp161948 handle_to_name(handle)); 340*90685d2cSjp161948 } 341*90685d2cSjp161948 } 342*90685d2cSjp161948 343*90685d2cSjp161948 static void 344*90685d2cSjp161948 handle_log_exit(void) 345*90685d2cSjp161948 { 346*90685d2cSjp161948 u_int i; 347*90685d2cSjp161948 348*90685d2cSjp161948 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) 349*90685d2cSjp161948 if (handles[i].use != HANDLE_UNUSED) 350*90685d2cSjp161948 handle_log_close(i, "forced"); 351*90685d2cSjp161948 } 352*90685d2cSjp161948 3537c478bd9Sstevel@tonic-gate static int 3547c478bd9Sstevel@tonic-gate get_handle(void) 3557c478bd9Sstevel@tonic-gate { 3567c478bd9Sstevel@tonic-gate char *handle; 3577c478bd9Sstevel@tonic-gate int val = -1; 3587c478bd9Sstevel@tonic-gate u_int hlen; 3597c478bd9Sstevel@tonic-gate 3607c478bd9Sstevel@tonic-gate handle = get_string(&hlen); 3617c478bd9Sstevel@tonic-gate if (hlen < 256) 3627c478bd9Sstevel@tonic-gate val = handle_from_string(handle, hlen); 3637c478bd9Sstevel@tonic-gate xfree(handle); 3647c478bd9Sstevel@tonic-gate return val; 3657c478bd9Sstevel@tonic-gate } 3667c478bd9Sstevel@tonic-gate 3677c478bd9Sstevel@tonic-gate /* send replies */ 3687c478bd9Sstevel@tonic-gate 3697c478bd9Sstevel@tonic-gate static void 3707c478bd9Sstevel@tonic-gate send_msg(Buffer *m) 3717c478bd9Sstevel@tonic-gate { 3727c478bd9Sstevel@tonic-gate int mlen = buffer_len(m); 3737c478bd9Sstevel@tonic-gate 3747c478bd9Sstevel@tonic-gate buffer_put_int(&oqueue, mlen); 3757c478bd9Sstevel@tonic-gate buffer_append(&oqueue, buffer_ptr(m), mlen); 3767c478bd9Sstevel@tonic-gate buffer_consume(m, mlen); 3777c478bd9Sstevel@tonic-gate } 3787c478bd9Sstevel@tonic-gate 379*90685d2cSjp161948 static const char * 380*90685d2cSjp161948 status_to_message(u_int32_t status) 3817c478bd9Sstevel@tonic-gate { 3827c478bd9Sstevel@tonic-gate const char *status_messages[] = { 3837c478bd9Sstevel@tonic-gate "Success", /* SSH_FX_OK */ 3847c478bd9Sstevel@tonic-gate "End of file", /* SSH_FX_EOF */ 3857c478bd9Sstevel@tonic-gate "No such file", /* SSH_FX_NO_SUCH_FILE */ 3867c478bd9Sstevel@tonic-gate "Permission denied", /* SSH_FX_PERMISSION_DENIED */ 3877c478bd9Sstevel@tonic-gate "Failure", /* SSH_FX_FAILURE */ 3887c478bd9Sstevel@tonic-gate "Bad message", /* SSH_FX_BAD_MESSAGE */ 3897c478bd9Sstevel@tonic-gate "No connection", /* SSH_FX_NO_CONNECTION */ 3907c478bd9Sstevel@tonic-gate "Connection lost", /* SSH_FX_CONNECTION_LOST */ 3917c478bd9Sstevel@tonic-gate "Operation unsupported", /* SSH_FX_OP_UNSUPPORTED */ 3927c478bd9Sstevel@tonic-gate "Unknown error" /* Others */ 3937c478bd9Sstevel@tonic-gate }; 394*90685d2cSjp161948 return (status_messages[MIN(status,SSH2_FX_MAX)]); 395*90685d2cSjp161948 } 3967c478bd9Sstevel@tonic-gate 397*90685d2cSjp161948 static void 398*90685d2cSjp161948 send_status(u_int32_t id, u_int32_t status) 399*90685d2cSjp161948 { 400*90685d2cSjp161948 Buffer msg; 401*90685d2cSjp161948 402*90685d2cSjp161948 debug3("request %u: sent status %u", id, status); 403*90685d2cSjp161948 if (log_level > SYSLOG_LEVEL_VERBOSE || 404*90685d2cSjp161948 (status != SSH2_FX_OK && status != SSH2_FX_EOF)) 405*90685d2cSjp161948 log("sent status %s", status_to_message(status)); 4067c478bd9Sstevel@tonic-gate buffer_init(&msg); 4077c478bd9Sstevel@tonic-gate buffer_put_char(&msg, SSH2_FXP_STATUS); 4087c478bd9Sstevel@tonic-gate buffer_put_int(&msg, id); 409*90685d2cSjp161948 buffer_put_int(&msg, status); 4107c478bd9Sstevel@tonic-gate if (version >= 3) { 411*90685d2cSjp161948 buffer_put_cstring(&msg, status_to_message(status)); 4127c478bd9Sstevel@tonic-gate buffer_put_cstring(&msg, ""); 4137c478bd9Sstevel@tonic-gate } 4147c478bd9Sstevel@tonic-gate send_msg(&msg); 4157c478bd9Sstevel@tonic-gate buffer_free(&msg); 4167c478bd9Sstevel@tonic-gate } 4177c478bd9Sstevel@tonic-gate static void 418*90685d2cSjp161948 send_data_or_handle(char type, u_int32_t id, const char *data, int dlen) 4197c478bd9Sstevel@tonic-gate { 4207c478bd9Sstevel@tonic-gate Buffer msg; 4217c478bd9Sstevel@tonic-gate 4227c478bd9Sstevel@tonic-gate buffer_init(&msg); 4237c478bd9Sstevel@tonic-gate buffer_put_char(&msg, type); 4247c478bd9Sstevel@tonic-gate buffer_put_int(&msg, id); 4257c478bd9Sstevel@tonic-gate buffer_put_string(&msg, data, dlen); 4267c478bd9Sstevel@tonic-gate send_msg(&msg); 4277c478bd9Sstevel@tonic-gate buffer_free(&msg); 4287c478bd9Sstevel@tonic-gate } 4297c478bd9Sstevel@tonic-gate 4307c478bd9Sstevel@tonic-gate static void 431*90685d2cSjp161948 send_data(u_int32_t id, const char *data, int dlen) 4327c478bd9Sstevel@tonic-gate { 433*90685d2cSjp161948 debug("request %u: sent data len %d", id, dlen); 4347c478bd9Sstevel@tonic-gate send_data_or_handle(SSH2_FXP_DATA, id, data, dlen); 4357c478bd9Sstevel@tonic-gate } 4367c478bd9Sstevel@tonic-gate 4377c478bd9Sstevel@tonic-gate static void 4387c478bd9Sstevel@tonic-gate send_handle(u_int32_t id, int handle) 4397c478bd9Sstevel@tonic-gate { 4407c478bd9Sstevel@tonic-gate char *string; 4417c478bd9Sstevel@tonic-gate int hlen; 4427c478bd9Sstevel@tonic-gate 4437c478bd9Sstevel@tonic-gate handle_to_string(handle, &string, &hlen); 444*90685d2cSjp161948 debug("request %u: sent handle handle %d", id, handle); 4457c478bd9Sstevel@tonic-gate send_data_or_handle(SSH2_FXP_HANDLE, id, string, hlen); 4467c478bd9Sstevel@tonic-gate xfree(string); 4477c478bd9Sstevel@tonic-gate } 4487c478bd9Sstevel@tonic-gate 4497c478bd9Sstevel@tonic-gate static void 450*90685d2cSjp161948 send_names(u_int32_t id, int count, const Stat *stats) 4517c478bd9Sstevel@tonic-gate { 4527c478bd9Sstevel@tonic-gate Buffer msg; 4537c478bd9Sstevel@tonic-gate int i; 4547c478bd9Sstevel@tonic-gate 4557c478bd9Sstevel@tonic-gate buffer_init(&msg); 4567c478bd9Sstevel@tonic-gate buffer_put_char(&msg, SSH2_FXP_NAME); 4577c478bd9Sstevel@tonic-gate buffer_put_int(&msg, id); 4587c478bd9Sstevel@tonic-gate buffer_put_int(&msg, count); 459*90685d2cSjp161948 debug("request %u: sent names count %d", id, count); 4607c478bd9Sstevel@tonic-gate for (i = 0; i < count; i++) { 4617c478bd9Sstevel@tonic-gate buffer_put_cstring(&msg, stats[i].name); 4627c478bd9Sstevel@tonic-gate buffer_put_cstring(&msg, stats[i].long_name); 4637c478bd9Sstevel@tonic-gate encode_attrib(&msg, &stats[i].attrib); 4647c478bd9Sstevel@tonic-gate } 4657c478bd9Sstevel@tonic-gate send_msg(&msg); 4667c478bd9Sstevel@tonic-gate buffer_free(&msg); 4677c478bd9Sstevel@tonic-gate } 4687c478bd9Sstevel@tonic-gate 4697c478bd9Sstevel@tonic-gate static void 470*90685d2cSjp161948 send_attrib(u_int32_t id, const Attrib *a) 4717c478bd9Sstevel@tonic-gate { 4727c478bd9Sstevel@tonic-gate Buffer msg; 4737c478bd9Sstevel@tonic-gate 474*90685d2cSjp161948 debug("request %u: sent attrib have 0x%x", id, a->flags); 4757c478bd9Sstevel@tonic-gate buffer_init(&msg); 4767c478bd9Sstevel@tonic-gate buffer_put_char(&msg, SSH2_FXP_ATTRS); 4777c478bd9Sstevel@tonic-gate buffer_put_int(&msg, id); 4787c478bd9Sstevel@tonic-gate encode_attrib(&msg, a); 4797c478bd9Sstevel@tonic-gate send_msg(&msg); 4807c478bd9Sstevel@tonic-gate buffer_free(&msg); 4817c478bd9Sstevel@tonic-gate } 4827c478bd9Sstevel@tonic-gate 4837c478bd9Sstevel@tonic-gate /* parse incoming */ 4847c478bd9Sstevel@tonic-gate 4857c478bd9Sstevel@tonic-gate static void 4867c478bd9Sstevel@tonic-gate process_init(void) 4877c478bd9Sstevel@tonic-gate { 4887c478bd9Sstevel@tonic-gate Buffer msg; 4897c478bd9Sstevel@tonic-gate 4907c478bd9Sstevel@tonic-gate version = get_int(); 491*90685d2cSjp161948 verbose("received client version %d", version); 4927c478bd9Sstevel@tonic-gate buffer_init(&msg); 4937c478bd9Sstevel@tonic-gate buffer_put_char(&msg, SSH2_FXP_VERSION); 4947c478bd9Sstevel@tonic-gate buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 4957c478bd9Sstevel@tonic-gate send_msg(&msg); 4967c478bd9Sstevel@tonic-gate buffer_free(&msg); 4977c478bd9Sstevel@tonic-gate } 4987c478bd9Sstevel@tonic-gate 4997c478bd9Sstevel@tonic-gate static void 5007c478bd9Sstevel@tonic-gate process_open(void) 5017c478bd9Sstevel@tonic-gate { 5027c478bd9Sstevel@tonic-gate u_int32_t id, pflags; 5037c478bd9Sstevel@tonic-gate Attrib *a; 5047c478bd9Sstevel@tonic-gate char *name; 5057c478bd9Sstevel@tonic-gate int handle, fd, flags, mode, status = SSH2_FX_FAILURE; 5067c478bd9Sstevel@tonic-gate 5077c478bd9Sstevel@tonic-gate id = get_int(); 5087c478bd9Sstevel@tonic-gate name = get_string(NULL); 5097c478bd9Sstevel@tonic-gate pflags = get_int(); /* portable flags */ 510*90685d2cSjp161948 debug3("request %u: open flags %d", id, pflags); 5117c478bd9Sstevel@tonic-gate a = get_attrib(); 5127c478bd9Sstevel@tonic-gate flags = flags_from_portable(pflags); 5137c478bd9Sstevel@tonic-gate mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? a->perm : 0666; 514*90685d2cSjp161948 log("open \"%s\" flags %s mode 0%o", 515*90685d2cSjp161948 name, string_from_portable(pflags), mode); 5167c478bd9Sstevel@tonic-gate fd = open(name, flags, mode); 5177c478bd9Sstevel@tonic-gate if (fd < 0) { 5187c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 5197c478bd9Sstevel@tonic-gate } else { 520*90685d2cSjp161948 handle = handle_new(HANDLE_FILE, name, fd, NULL); 5217c478bd9Sstevel@tonic-gate if (handle < 0) { 5227c478bd9Sstevel@tonic-gate close(fd); 5237c478bd9Sstevel@tonic-gate } else { 5247c478bd9Sstevel@tonic-gate send_handle(id, handle); 5257c478bd9Sstevel@tonic-gate status = SSH2_FX_OK; 5267c478bd9Sstevel@tonic-gate } 5277c478bd9Sstevel@tonic-gate } 5287c478bd9Sstevel@tonic-gate if (status != SSH2_FX_OK) 5297c478bd9Sstevel@tonic-gate send_status(id, status); 5307c478bd9Sstevel@tonic-gate xfree(name); 5317c478bd9Sstevel@tonic-gate } 5327c478bd9Sstevel@tonic-gate 5337c478bd9Sstevel@tonic-gate static void 5347c478bd9Sstevel@tonic-gate process_close(void) 5357c478bd9Sstevel@tonic-gate { 5367c478bd9Sstevel@tonic-gate u_int32_t id; 5377c478bd9Sstevel@tonic-gate int handle, ret, status = SSH2_FX_FAILURE; 5387c478bd9Sstevel@tonic-gate 5397c478bd9Sstevel@tonic-gate id = get_int(); 5407c478bd9Sstevel@tonic-gate handle = get_handle(); 541*90685d2cSjp161948 debug3("request %u: close handle %u", id, handle); 542*90685d2cSjp161948 handle_log_close(handle, NULL); 5437c478bd9Sstevel@tonic-gate ret = handle_close(handle); 5447c478bd9Sstevel@tonic-gate status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 5457c478bd9Sstevel@tonic-gate send_status(id, status); 5467c478bd9Sstevel@tonic-gate } 5477c478bd9Sstevel@tonic-gate 5487c478bd9Sstevel@tonic-gate static void 5497c478bd9Sstevel@tonic-gate process_read(void) 5507c478bd9Sstevel@tonic-gate { 5517c478bd9Sstevel@tonic-gate char buf[64*1024]; 5527c478bd9Sstevel@tonic-gate u_int32_t id, len; 5537c478bd9Sstevel@tonic-gate int handle, fd, ret, status = SSH2_FX_FAILURE; 5547c478bd9Sstevel@tonic-gate u_int64_t off; 5557c478bd9Sstevel@tonic-gate 5567c478bd9Sstevel@tonic-gate id = get_int(); 5577c478bd9Sstevel@tonic-gate handle = get_handle(); 5587c478bd9Sstevel@tonic-gate off = get_int64(); 5597c478bd9Sstevel@tonic-gate len = get_int(); 5607c478bd9Sstevel@tonic-gate 561*90685d2cSjp161948 debug("request %u: read \"%s\" (handle %d) off %llu len %d", 562*90685d2cSjp161948 id, handle_to_name(handle), handle, (unsigned long long)off, len); 5637c478bd9Sstevel@tonic-gate if (len > sizeof buf) { 5647c478bd9Sstevel@tonic-gate len = sizeof buf; 565*90685d2cSjp161948 debug2("read change len %d", len); 5667c478bd9Sstevel@tonic-gate } 5677c478bd9Sstevel@tonic-gate fd = handle_to_fd(handle); 5687c478bd9Sstevel@tonic-gate if (fd >= 0) { 5697c478bd9Sstevel@tonic-gate if (lseek(fd, off, SEEK_SET) < 0) { 5707c478bd9Sstevel@tonic-gate error("process_read: seek failed"); 5717c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 5727c478bd9Sstevel@tonic-gate } else { 5737c478bd9Sstevel@tonic-gate ret = read(fd, buf, len); 5747c478bd9Sstevel@tonic-gate if (ret < 0) { 5757c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 5767c478bd9Sstevel@tonic-gate } else if (ret == 0) { 5777c478bd9Sstevel@tonic-gate status = SSH2_FX_EOF; 5787c478bd9Sstevel@tonic-gate } else { 5797c478bd9Sstevel@tonic-gate send_data(id, buf, ret); 5807c478bd9Sstevel@tonic-gate status = SSH2_FX_OK; 581*90685d2cSjp161948 handle_update_read(handle, ret); 5827c478bd9Sstevel@tonic-gate } 5837c478bd9Sstevel@tonic-gate } 5847c478bd9Sstevel@tonic-gate } 5857c478bd9Sstevel@tonic-gate if (status != SSH2_FX_OK) 5867c478bd9Sstevel@tonic-gate send_status(id, status); 5877c478bd9Sstevel@tonic-gate } 5887c478bd9Sstevel@tonic-gate 5897c478bd9Sstevel@tonic-gate static void 5907c478bd9Sstevel@tonic-gate process_write(void) 5917c478bd9Sstevel@tonic-gate { 5927c478bd9Sstevel@tonic-gate u_int32_t id; 5937c478bd9Sstevel@tonic-gate u_int64_t off; 5947c478bd9Sstevel@tonic-gate u_int len; 5957c478bd9Sstevel@tonic-gate int handle, fd, ret, status = SSH2_FX_FAILURE; 5967c478bd9Sstevel@tonic-gate char *data; 5977c478bd9Sstevel@tonic-gate 5987c478bd9Sstevel@tonic-gate id = get_int(); 5997c478bd9Sstevel@tonic-gate handle = get_handle(); 6007c478bd9Sstevel@tonic-gate off = get_int64(); 6017c478bd9Sstevel@tonic-gate data = get_string(&len); 6027c478bd9Sstevel@tonic-gate 603*90685d2cSjp161948 debug("request %u: write \"%s\" (handle %d) off %llu len %d", 604*90685d2cSjp161948 id, handle_to_name(handle), handle, (unsigned long long)off, len); 6057c478bd9Sstevel@tonic-gate fd = handle_to_fd(handle); 6067c478bd9Sstevel@tonic-gate if (fd >= 0) { 6077c478bd9Sstevel@tonic-gate if (lseek(fd, off, SEEK_SET) < 0) { 6087c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 6097c478bd9Sstevel@tonic-gate error("process_write: seek failed"); 6107c478bd9Sstevel@tonic-gate } else { 6117c478bd9Sstevel@tonic-gate /* XXX ATOMICIO ? */ 6127c478bd9Sstevel@tonic-gate ret = write(fd, data, len); 613*90685d2cSjp161948 if (ret < 0) { 6147c478bd9Sstevel@tonic-gate error("process_write: write failed"); 6157c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 616*90685d2cSjp161948 } else if ((size_t)ret == len) { 6177c478bd9Sstevel@tonic-gate status = SSH2_FX_OK; 618*90685d2cSjp161948 handle_update_write(handle, ret); 6197c478bd9Sstevel@tonic-gate } else { 620*90685d2cSjp161948 debug2("nothing at all written"); 6217c478bd9Sstevel@tonic-gate } 6227c478bd9Sstevel@tonic-gate } 6237c478bd9Sstevel@tonic-gate } 6247c478bd9Sstevel@tonic-gate send_status(id, status); 6257c478bd9Sstevel@tonic-gate xfree(data); 6267c478bd9Sstevel@tonic-gate } 6277c478bd9Sstevel@tonic-gate 6287c478bd9Sstevel@tonic-gate static void 6297c478bd9Sstevel@tonic-gate process_do_stat(int do_lstat) 6307c478bd9Sstevel@tonic-gate { 6317c478bd9Sstevel@tonic-gate Attrib a; 6327c478bd9Sstevel@tonic-gate struct stat st; 6337c478bd9Sstevel@tonic-gate u_int32_t id; 6347c478bd9Sstevel@tonic-gate char *name; 6357c478bd9Sstevel@tonic-gate int ret, status = SSH2_FX_FAILURE; 6367c478bd9Sstevel@tonic-gate 6377c478bd9Sstevel@tonic-gate id = get_int(); 6387c478bd9Sstevel@tonic-gate name = get_string(NULL); 639*90685d2cSjp161948 debug3("request %u: %sstat", id, do_lstat ? "l" : ""); 640*90685d2cSjp161948 verbose("%sstat name \"%s\"", do_lstat ? "l" : "", name); 6417c478bd9Sstevel@tonic-gate ret = do_lstat ? lstat(name, &st) : stat(name, &st); 6427c478bd9Sstevel@tonic-gate if (ret < 0) { 6437c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 6447c478bd9Sstevel@tonic-gate } else { 6457c478bd9Sstevel@tonic-gate stat_to_attrib(&st, &a); 6467c478bd9Sstevel@tonic-gate send_attrib(id, &a); 6477c478bd9Sstevel@tonic-gate status = SSH2_FX_OK; 6487c478bd9Sstevel@tonic-gate } 6497c478bd9Sstevel@tonic-gate if (status != SSH2_FX_OK) 6507c478bd9Sstevel@tonic-gate send_status(id, status); 6517c478bd9Sstevel@tonic-gate xfree(name); 6527c478bd9Sstevel@tonic-gate } 6537c478bd9Sstevel@tonic-gate 6547c478bd9Sstevel@tonic-gate static void 6557c478bd9Sstevel@tonic-gate process_stat(void) 6567c478bd9Sstevel@tonic-gate { 6577c478bd9Sstevel@tonic-gate process_do_stat(0); 6587c478bd9Sstevel@tonic-gate } 6597c478bd9Sstevel@tonic-gate 6607c478bd9Sstevel@tonic-gate static void 6617c478bd9Sstevel@tonic-gate process_lstat(void) 6627c478bd9Sstevel@tonic-gate { 6637c478bd9Sstevel@tonic-gate process_do_stat(1); 6647c478bd9Sstevel@tonic-gate } 6657c478bd9Sstevel@tonic-gate 6667c478bd9Sstevel@tonic-gate static void 6677c478bd9Sstevel@tonic-gate process_fstat(void) 6687c478bd9Sstevel@tonic-gate { 6697c478bd9Sstevel@tonic-gate Attrib a; 6707c478bd9Sstevel@tonic-gate struct stat st; 6717c478bd9Sstevel@tonic-gate u_int32_t id; 6727c478bd9Sstevel@tonic-gate int fd, ret, handle, status = SSH2_FX_FAILURE; 6737c478bd9Sstevel@tonic-gate 6747c478bd9Sstevel@tonic-gate id = get_int(); 6757c478bd9Sstevel@tonic-gate handle = get_handle(); 676*90685d2cSjp161948 debug("request %u: fstat \"%s\" (handle %u)", 677*90685d2cSjp161948 id, handle_to_name(handle), handle); 6787c478bd9Sstevel@tonic-gate fd = handle_to_fd(handle); 6797c478bd9Sstevel@tonic-gate if (fd >= 0) { 6807c478bd9Sstevel@tonic-gate ret = fstat(fd, &st); 6817c478bd9Sstevel@tonic-gate if (ret < 0) { 6827c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 6837c478bd9Sstevel@tonic-gate } else { 6847c478bd9Sstevel@tonic-gate stat_to_attrib(&st, &a); 6857c478bd9Sstevel@tonic-gate send_attrib(id, &a); 6867c478bd9Sstevel@tonic-gate status = SSH2_FX_OK; 6877c478bd9Sstevel@tonic-gate } 6887c478bd9Sstevel@tonic-gate } 6897c478bd9Sstevel@tonic-gate if (status != SSH2_FX_OK) 6907c478bd9Sstevel@tonic-gate send_status(id, status); 6917c478bd9Sstevel@tonic-gate } 6927c478bd9Sstevel@tonic-gate 6937c478bd9Sstevel@tonic-gate static struct timeval * 694*90685d2cSjp161948 attrib_to_tv(const Attrib *a) 6957c478bd9Sstevel@tonic-gate { 6967c478bd9Sstevel@tonic-gate static struct timeval tv[2]; 6977c478bd9Sstevel@tonic-gate 6987c478bd9Sstevel@tonic-gate tv[0].tv_sec = a->atime; 6997c478bd9Sstevel@tonic-gate tv[0].tv_usec = 0; 7007c478bd9Sstevel@tonic-gate tv[1].tv_sec = a->mtime; 7017c478bd9Sstevel@tonic-gate tv[1].tv_usec = 0; 7027c478bd9Sstevel@tonic-gate return tv; 7037c478bd9Sstevel@tonic-gate } 7047c478bd9Sstevel@tonic-gate 7057c478bd9Sstevel@tonic-gate static void 7067c478bd9Sstevel@tonic-gate process_setstat(void) 7077c478bd9Sstevel@tonic-gate { 7087c478bd9Sstevel@tonic-gate Attrib *a; 7097c478bd9Sstevel@tonic-gate u_int32_t id; 7107c478bd9Sstevel@tonic-gate char *name; 7117c478bd9Sstevel@tonic-gate int status = SSH2_FX_OK, ret; 7127c478bd9Sstevel@tonic-gate 7137c478bd9Sstevel@tonic-gate id = get_int(); 7147c478bd9Sstevel@tonic-gate name = get_string(NULL); 7157c478bd9Sstevel@tonic-gate a = get_attrib(); 716*90685d2cSjp161948 debug("request %u: setstat name \"%s\"", id, name); 7177c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 718*90685d2cSjp161948 log("set \"%s\" size %llu", 719*90685d2cSjp161948 name, (unsigned long long)a->size); 7207c478bd9Sstevel@tonic-gate ret = truncate(name, a->size); 7217c478bd9Sstevel@tonic-gate if (ret == -1) 7227c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 7237c478bd9Sstevel@tonic-gate } 7247c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 725*90685d2cSjp161948 log("set \"%s\" mode %04o", name, a->perm); 7267c478bd9Sstevel@tonic-gate ret = chmod(name, a->perm & 0777); 7277c478bd9Sstevel@tonic-gate if (ret == -1) 7287c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 7297c478bd9Sstevel@tonic-gate } 7307c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 731*90685d2cSjp161948 char buf[64]; 732*90685d2cSjp161948 time_t t = a->mtime; 733*90685d2cSjp161948 734*90685d2cSjp161948 strftime(buf, sizeof(buf), "%Y" "%m%d-%H:%M:%S", 735*90685d2cSjp161948 localtime(&t)); 736*90685d2cSjp161948 log("set \"%s\" modtime %s", name, buf); 7377c478bd9Sstevel@tonic-gate ret = utimes(name, attrib_to_tv(a)); 7387c478bd9Sstevel@tonic-gate if (ret == -1) 7397c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 7407c478bd9Sstevel@tonic-gate } 7417c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 742*90685d2cSjp161948 log("set \"%s\" owner %lu group %lu", name, 743*90685d2cSjp161948 (u_long)a->uid, (u_long)a->gid); 7447c478bd9Sstevel@tonic-gate ret = chown(name, a->uid, a->gid); 7457c478bd9Sstevel@tonic-gate if (ret == -1) 7467c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 7477c478bd9Sstevel@tonic-gate } 7487c478bd9Sstevel@tonic-gate send_status(id, status); 7497c478bd9Sstevel@tonic-gate xfree(name); 7507c478bd9Sstevel@tonic-gate } 7517c478bd9Sstevel@tonic-gate 7527c478bd9Sstevel@tonic-gate static void 7537c478bd9Sstevel@tonic-gate process_fsetstat(void) 7547c478bd9Sstevel@tonic-gate { 7557c478bd9Sstevel@tonic-gate Attrib *a; 7567c478bd9Sstevel@tonic-gate u_int32_t id; 7577c478bd9Sstevel@tonic-gate int handle, fd, ret; 7587c478bd9Sstevel@tonic-gate int status = SSH2_FX_OK; 7597c478bd9Sstevel@tonic-gate 7607c478bd9Sstevel@tonic-gate id = get_int(); 7617c478bd9Sstevel@tonic-gate handle = get_handle(); 7627c478bd9Sstevel@tonic-gate a = get_attrib(); 763*90685d2cSjp161948 debug("request %u: fsetstat handle %d", id, handle); 7647c478bd9Sstevel@tonic-gate fd = handle_to_fd(handle); 765*90685d2cSjp161948 if (fd < 0) { 7667c478bd9Sstevel@tonic-gate status = SSH2_FX_FAILURE; 7677c478bd9Sstevel@tonic-gate } else { 768*90685d2cSjp161948 char *name = handle_to_name(handle); 769*90685d2cSjp161948 7707c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { 771*90685d2cSjp161948 log("set \"%s\" size %llu", 772*90685d2cSjp161948 name, (unsigned long long)a->size); 7737c478bd9Sstevel@tonic-gate ret = ftruncate(fd, a->size); 7747c478bd9Sstevel@tonic-gate if (ret == -1) 7757c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 7767c478bd9Sstevel@tonic-gate } 7777c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 778*90685d2cSjp161948 log("set \"%s\" mode %04o", name, a->perm); 7797c478bd9Sstevel@tonic-gate #ifdef HAVE_FCHMOD 7807c478bd9Sstevel@tonic-gate ret = fchmod(fd, a->perm & 0777); 7817c478bd9Sstevel@tonic-gate #else 7827c478bd9Sstevel@tonic-gate ret = chmod(name, a->perm & 0777); 7837c478bd9Sstevel@tonic-gate #endif 7847c478bd9Sstevel@tonic-gate if (ret == -1) 7857c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 7867c478bd9Sstevel@tonic-gate } 7877c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { 788*90685d2cSjp161948 char buf[64]; 789*90685d2cSjp161948 time_t t = a->mtime; 790*90685d2cSjp161948 791*90685d2cSjp161948 strftime(buf, sizeof(buf), "%Y" "%m%d-%H:%M:%S", 792*90685d2cSjp161948 localtime(&t)); 793*90685d2cSjp161948 log("set \"%s\" modtime %s", name, buf); 7947c478bd9Sstevel@tonic-gate #ifdef HAVE_FUTIMES 7957c478bd9Sstevel@tonic-gate ret = futimes(fd, attrib_to_tv(a)); 7967c478bd9Sstevel@tonic-gate #else 7977c478bd9Sstevel@tonic-gate ret = utimes(name, attrib_to_tv(a)); 7987c478bd9Sstevel@tonic-gate #endif 7997c478bd9Sstevel@tonic-gate if (ret == -1) 8007c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 8017c478bd9Sstevel@tonic-gate } 8027c478bd9Sstevel@tonic-gate if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { 803*90685d2cSjp161948 log("set \"%s\" owner %lu group %lu", name, 804*90685d2cSjp161948 (u_long)a->uid, (u_long)a->gid); 8057c478bd9Sstevel@tonic-gate #ifdef HAVE_FCHOWN 8067c478bd9Sstevel@tonic-gate ret = fchown(fd, a->uid, a->gid); 8077c478bd9Sstevel@tonic-gate #else 8087c478bd9Sstevel@tonic-gate ret = chown(name, a->uid, a->gid); 8097c478bd9Sstevel@tonic-gate #endif 8107c478bd9Sstevel@tonic-gate if (ret == -1) 8117c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 8127c478bd9Sstevel@tonic-gate } 8137c478bd9Sstevel@tonic-gate } 8147c478bd9Sstevel@tonic-gate send_status(id, status); 8157c478bd9Sstevel@tonic-gate } 8167c478bd9Sstevel@tonic-gate 8177c478bd9Sstevel@tonic-gate static void 8187c478bd9Sstevel@tonic-gate process_opendir(void) 8197c478bd9Sstevel@tonic-gate { 8207c478bd9Sstevel@tonic-gate DIR *dirp = NULL; 8217c478bd9Sstevel@tonic-gate char *path; 8227c478bd9Sstevel@tonic-gate int handle, status = SSH2_FX_FAILURE; 8237c478bd9Sstevel@tonic-gate u_int32_t id; 8247c478bd9Sstevel@tonic-gate 8257c478bd9Sstevel@tonic-gate id = get_int(); 8267c478bd9Sstevel@tonic-gate path = get_string(NULL); 827*90685d2cSjp161948 debug3("request %u: opendir", id); 828*90685d2cSjp161948 log("opendir \"%s\"", path); 8297c478bd9Sstevel@tonic-gate dirp = opendir(path); 8307c478bd9Sstevel@tonic-gate if (dirp == NULL) { 8317c478bd9Sstevel@tonic-gate status = errno_to_portable(errno); 8327c478bd9Sstevel@tonic-gate } else { 833*90685d2cSjp161948 handle = handle_new(HANDLE_DIR, path, 0, dirp); 8347c478bd9Sstevel@tonic-gate if (handle < 0) { 8357c478bd9Sstevel@tonic-gate closedir(dirp); 8367c478bd9Sstevel@tonic-gate } else { 8377c478bd9Sstevel@tonic-gate send_handle(id, handle); 8387c478bd9Sstevel@tonic-gate status = SSH2_FX_OK; 8397c478bd9Sstevel@tonic-gate } 8407c478bd9Sstevel@tonic-gate 8417c478bd9Sstevel@tonic-gate } 8427c478bd9Sstevel@tonic-gate if (status != SSH2_FX_OK) 8437c478bd9Sstevel@tonic-gate send_status(id, status); 8447c478bd9Sstevel@tonic-gate xfree(path); 8457c478bd9Sstevel@tonic-gate } 8467c478bd9Sstevel@tonic-gate 8477c478bd9Sstevel@tonic-gate static void 8487c478bd9Sstevel@tonic-gate process_readdir(void) 8497c478bd9Sstevel@tonic-gate { 8507c478bd9Sstevel@tonic-gate DIR *dirp; 8517c478bd9Sstevel@tonic-gate struct dirent *dp; 8527c478bd9Sstevel@tonic-gate char *path; 8537c478bd9Sstevel@tonic-gate int handle; 8547c478bd9Sstevel@tonic-gate u_int32_t id; 8557c478bd9Sstevel@tonic-gate 8567c478bd9Sstevel@tonic-gate id = get_int(); 8577c478bd9Sstevel@tonic-gate handle = get_handle(); 858*90685d2cSjp161948 debug("request %u: readdir \"%s\" (handle %d)", id, 859*90685d2cSjp161948 handle_to_name(handle), handle); 8607c478bd9Sstevel@tonic-gate dirp = handle_to_dir(handle); 8617c478bd9Sstevel@tonic-gate path = handle_to_name(handle); 8627c478bd9Sstevel@tonic-gate if (dirp == NULL || path == NULL) { 8637c478bd9Sstevel@tonic-gate send_status(id, SSH2_FX_FAILURE); 8647c478bd9Sstevel@tonic-gate } else { 8657c478bd9Sstevel@tonic-gate struct stat st; 866*90685d2cSjp161948 char pathname[MAXPATHLEN]; 8677c478bd9Sstevel@tonic-gate Stat *stats; 8687c478bd9Sstevel@tonic-gate int nstats = 10, count = 0, i; 8697c478bd9Sstevel@tonic-gate 870*90685d2cSjp161948 stats = xcalloc(nstats, sizeof(Stat)); 8717c478bd9Sstevel@tonic-gate while ((dp = readdir(dirp)) != NULL) { 8727c478bd9Sstevel@tonic-gate if (count >= nstats) { 8737c478bd9Sstevel@tonic-gate nstats *= 2; 8747c478bd9Sstevel@tonic-gate stats = xrealloc(stats, nstats * sizeof(Stat)); 8757c478bd9Sstevel@tonic-gate } 8767c478bd9Sstevel@tonic-gate /* XXX OVERFLOW ? */ 8777c478bd9Sstevel@tonic-gate snprintf(pathname, sizeof pathname, "%s%s%s", path, 8787c478bd9Sstevel@tonic-gate strcmp(path, "/") ? "/" : "", dp->d_name); 8797c478bd9Sstevel@tonic-gate if (lstat(pathname, &st) < 0) 8807c478bd9Sstevel@tonic-gate continue; 8817c478bd9Sstevel@tonic-gate stat_to_attrib(&st, &(stats[count].attrib)); 8827c478bd9Sstevel@tonic-gate stats[count].name = xstrdup(dp->d_name); 8837c478bd9Sstevel@tonic-gate stats[count].long_name = ls_file(dp->d_name, &st, 0); 8847c478bd9Sstevel@tonic-gate count++; 8857c478bd9Sstevel@tonic-gate /* send up to 100 entries in one message */ 8867c478bd9Sstevel@tonic-gate /* XXX check packet size instead */ 8877c478bd9Sstevel@tonic-gate if (count == 100) 8887c478bd9Sstevel@tonic-gate break; 8897c478bd9Sstevel@tonic-gate } 8907c478bd9Sstevel@tonic-gate if (count > 0) { 8917c478bd9Sstevel@tonic-gate send_names(id, count, stats); 8927c478bd9Sstevel@tonic-gate for (i = 0; i < count; i++) { 8937c478bd9Sstevel@tonic-gate xfree(stats[i].name); 8947c478bd9Sstevel@tonic-gate xfree(stats[i].long_name); 8957c478bd9Sstevel@tonic-gate } 8967c478bd9Sstevel@tonic-gate } else { 8977c478bd9Sstevel@tonic-gate send_status(id, SSH2_FX_EOF); 8987c478bd9Sstevel@tonic-gate } 8997c478bd9Sstevel@tonic-gate xfree(stats); 9007c478bd9Sstevel@tonic-gate } 9017c478bd9Sstevel@tonic-gate } 9027c478bd9Sstevel@tonic-gate 9037c478bd9Sstevel@tonic-gate static void 9047c478bd9Sstevel@tonic-gate process_remove(void) 9057c478bd9Sstevel@tonic-gate { 9067c478bd9Sstevel@tonic-gate char *name; 9077c478bd9Sstevel@tonic-gate u_int32_t id; 9087c478bd9Sstevel@tonic-gate int status = SSH2_FX_FAILURE; 9097c478bd9Sstevel@tonic-gate int ret; 9107c478bd9Sstevel@tonic-gate 9117c478bd9Sstevel@tonic-gate id = get_int(); 9127c478bd9Sstevel@tonic-gate name = get_string(NULL); 913*90685d2cSjp161948 debug3("request %u: remove", id); 914*90685d2cSjp161948 log("remove name \"%s\"", name); 9157c478bd9Sstevel@tonic-gate ret = unlink(name); 9167c478bd9Sstevel@tonic-gate status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 9177c478bd9Sstevel@tonic-gate send_status(id, status); 9187c478bd9Sstevel@tonic-gate xfree(name); 9197c478bd9Sstevel@tonic-gate } 9207c478bd9Sstevel@tonic-gate 9217c478bd9Sstevel@tonic-gate static void 9227c478bd9Sstevel@tonic-gate process_mkdir(void) 9237c478bd9Sstevel@tonic-gate { 9247c478bd9Sstevel@tonic-gate Attrib *a; 9257c478bd9Sstevel@tonic-gate u_int32_t id; 9267c478bd9Sstevel@tonic-gate char *name; 9277c478bd9Sstevel@tonic-gate int ret, mode, status = SSH2_FX_FAILURE; 9287c478bd9Sstevel@tonic-gate 9297c478bd9Sstevel@tonic-gate id = get_int(); 9307c478bd9Sstevel@tonic-gate name = get_string(NULL); 9317c478bd9Sstevel@tonic-gate a = get_attrib(); 9327c478bd9Sstevel@tonic-gate mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 9337c478bd9Sstevel@tonic-gate a->perm & 0777 : 0777; 934*90685d2cSjp161948 debug3("request %u: mkdir", id); 935*90685d2cSjp161948 log("mkdir name \"%s\" mode 0%o", name, mode); 9367c478bd9Sstevel@tonic-gate ret = mkdir(name, mode); 9377c478bd9Sstevel@tonic-gate status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 9387c478bd9Sstevel@tonic-gate send_status(id, status); 9397c478bd9Sstevel@tonic-gate xfree(name); 9407c478bd9Sstevel@tonic-gate } 9417c478bd9Sstevel@tonic-gate 9427c478bd9Sstevel@tonic-gate static void 9437c478bd9Sstevel@tonic-gate process_rmdir(void) 9447c478bd9Sstevel@tonic-gate { 9457c478bd9Sstevel@tonic-gate u_int32_t id; 9467c478bd9Sstevel@tonic-gate char *name; 9477c478bd9Sstevel@tonic-gate int ret, status; 9487c478bd9Sstevel@tonic-gate 9497c478bd9Sstevel@tonic-gate id = get_int(); 9507c478bd9Sstevel@tonic-gate name = get_string(NULL); 951*90685d2cSjp161948 debug3("request %u: rmdir", id); 952*90685d2cSjp161948 log("rmdir name \"%s\"", name); 9537c478bd9Sstevel@tonic-gate ret = rmdir(name); 9547c478bd9Sstevel@tonic-gate status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 9557c478bd9Sstevel@tonic-gate send_status(id, status); 9567c478bd9Sstevel@tonic-gate xfree(name); 9577c478bd9Sstevel@tonic-gate } 9587c478bd9Sstevel@tonic-gate 9597c478bd9Sstevel@tonic-gate static void 9607c478bd9Sstevel@tonic-gate process_realpath(void) 9617c478bd9Sstevel@tonic-gate { 9627c478bd9Sstevel@tonic-gate char resolvedname[MAXPATHLEN]; 9637c478bd9Sstevel@tonic-gate u_int32_t id; 9647c478bd9Sstevel@tonic-gate char *path; 9657c478bd9Sstevel@tonic-gate 9667c478bd9Sstevel@tonic-gate id = get_int(); 9677c478bd9Sstevel@tonic-gate path = get_string(NULL); 9687c478bd9Sstevel@tonic-gate if (path[0] == '\0') { 9697c478bd9Sstevel@tonic-gate xfree(path); 9707c478bd9Sstevel@tonic-gate path = xstrdup("."); 9717c478bd9Sstevel@tonic-gate } 972*90685d2cSjp161948 debug3("request %u: realpath", id); 973*90685d2cSjp161948 verbose("realpath \"%s\"", path); 9747c478bd9Sstevel@tonic-gate if (realpath(path, resolvedname) == NULL) { 9757c478bd9Sstevel@tonic-gate send_status(id, errno_to_portable(errno)); 9767c478bd9Sstevel@tonic-gate } else { 9777c478bd9Sstevel@tonic-gate Stat s; 9787c478bd9Sstevel@tonic-gate attrib_clear(&s.attrib); 9797c478bd9Sstevel@tonic-gate s.name = s.long_name = resolvedname; 9807c478bd9Sstevel@tonic-gate send_names(id, 1, &s); 9817c478bd9Sstevel@tonic-gate } 9827c478bd9Sstevel@tonic-gate xfree(path); 9837c478bd9Sstevel@tonic-gate } 9847c478bd9Sstevel@tonic-gate 9857c478bd9Sstevel@tonic-gate static void 9867c478bd9Sstevel@tonic-gate process_rename(void) 9877c478bd9Sstevel@tonic-gate { 9887c478bd9Sstevel@tonic-gate u_int32_t id; 9897c478bd9Sstevel@tonic-gate char *oldpath, *newpath; 990*90685d2cSjp161948 int status; 991*90685d2cSjp161948 struct stat sb; 9927c478bd9Sstevel@tonic-gate 9937c478bd9Sstevel@tonic-gate id = get_int(); 9947c478bd9Sstevel@tonic-gate oldpath = get_string(NULL); 9957c478bd9Sstevel@tonic-gate newpath = get_string(NULL); 996*90685d2cSjp161948 debug3("request %u: rename", id); 997*90685d2cSjp161948 log("rename old \"%s\" new \"%s\"", oldpath, newpath); 998*90685d2cSjp161948 status = SSH2_FX_FAILURE; 999*90685d2cSjp161948 if (lstat(oldpath, &sb) == -1) 1000*90685d2cSjp161948 status = errno_to_portable(errno); 1001*90685d2cSjp161948 else if (S_ISREG(sb.st_mode)) { 1002*90685d2cSjp161948 /* Race-free rename of regular files */ 1003*90685d2cSjp161948 if (link(oldpath, newpath) == -1) { 1004*90685d2cSjp161948 if (errno == EOPNOTSUPP 1005*90685d2cSjp161948 #ifdef LINK_OPNOTSUPP_ERRNO 1006*90685d2cSjp161948 || errno == LINK_OPNOTSUPP_ERRNO 1007*90685d2cSjp161948 #endif 1008*90685d2cSjp161948 ) { 1009*90685d2cSjp161948 struct stat st; 1010*90685d2cSjp161948 1011*90685d2cSjp161948 /* 1012*90685d2cSjp161948 * fs doesn't support links, so fall back to 1013*90685d2cSjp161948 * stat+rename. This is racy. 1014*90685d2cSjp161948 */ 10157c478bd9Sstevel@tonic-gate if (stat(newpath, &st) == -1) { 1016*90685d2cSjp161948 if (rename(oldpath, newpath) == -1) 1017*90685d2cSjp161948 status = 1018*90685d2cSjp161948 errno_to_portable(errno); 1019*90685d2cSjp161948 else 1020*90685d2cSjp161948 status = SSH2_FX_OK; 1021*90685d2cSjp161948 } 1022*90685d2cSjp161948 } else { 1023*90685d2cSjp161948 status = errno_to_portable(errno); 1024*90685d2cSjp161948 } 1025*90685d2cSjp161948 } else if (unlink(oldpath) == -1) { 1026*90685d2cSjp161948 status = errno_to_portable(errno); 1027*90685d2cSjp161948 /* clean spare link */ 1028*90685d2cSjp161948 unlink(newpath); 1029*90685d2cSjp161948 } else 1030*90685d2cSjp161948 status = SSH2_FX_OK; 1031*90685d2cSjp161948 } else if (stat(newpath, &sb) == -1) { 1032*90685d2cSjp161948 if (rename(oldpath, newpath) == -1) 1033*90685d2cSjp161948 status = errno_to_portable(errno); 1034*90685d2cSjp161948 else 1035*90685d2cSjp161948 status = SSH2_FX_OK; 10367c478bd9Sstevel@tonic-gate } 10377c478bd9Sstevel@tonic-gate send_status(id, status); 10387c478bd9Sstevel@tonic-gate xfree(oldpath); 10397c478bd9Sstevel@tonic-gate xfree(newpath); 10407c478bd9Sstevel@tonic-gate } 10417c478bd9Sstevel@tonic-gate 10427c478bd9Sstevel@tonic-gate static void 10437c478bd9Sstevel@tonic-gate process_readlink(void) 10447c478bd9Sstevel@tonic-gate { 10457c478bd9Sstevel@tonic-gate u_int32_t id; 10467c478bd9Sstevel@tonic-gate int len; 1047*90685d2cSjp161948 char buf[MAXPATHLEN]; 10487c478bd9Sstevel@tonic-gate char *path; 10497c478bd9Sstevel@tonic-gate 10507c478bd9Sstevel@tonic-gate id = get_int(); 10517c478bd9Sstevel@tonic-gate path = get_string(NULL); 1052*90685d2cSjp161948 debug3("request %u: readlink", id); 1053*90685d2cSjp161948 verbose("readlink \"%s\"", path); 1054*90685d2cSjp161948 if ((len = readlink(path, buf, sizeof(buf) - 1)) == -1) 10557c478bd9Sstevel@tonic-gate send_status(id, errno_to_portable(errno)); 10567c478bd9Sstevel@tonic-gate else { 10577c478bd9Sstevel@tonic-gate Stat s; 10587c478bd9Sstevel@tonic-gate 1059*90685d2cSjp161948 buf[len] = '\0'; 10607c478bd9Sstevel@tonic-gate attrib_clear(&s.attrib); 1061*90685d2cSjp161948 s.name = s.long_name = buf; 10627c478bd9Sstevel@tonic-gate send_names(id, 1, &s); 10637c478bd9Sstevel@tonic-gate } 10647c478bd9Sstevel@tonic-gate xfree(path); 10657c478bd9Sstevel@tonic-gate } 10667c478bd9Sstevel@tonic-gate 10677c478bd9Sstevel@tonic-gate static void 10687c478bd9Sstevel@tonic-gate process_symlink(void) 10697c478bd9Sstevel@tonic-gate { 10707c478bd9Sstevel@tonic-gate u_int32_t id; 10717c478bd9Sstevel@tonic-gate char *oldpath, *newpath; 1072*90685d2cSjp161948 int ret, status; 10737c478bd9Sstevel@tonic-gate 10747c478bd9Sstevel@tonic-gate id = get_int(); 10757c478bd9Sstevel@tonic-gate oldpath = get_string(NULL); 10767c478bd9Sstevel@tonic-gate newpath = get_string(NULL); 1077*90685d2cSjp161948 debug3("request %u: symlink", id); 1078*90685d2cSjp161948 log("symlink old \"%s\" new \"%s\"", oldpath, newpath); 1079*90685d2cSjp161948 /* this will fail if 'newpath' exists */ 10807c478bd9Sstevel@tonic-gate ret = symlink(oldpath, newpath); 10817c478bd9Sstevel@tonic-gate status = (ret == -1) ? errno_to_portable(errno) : SSH2_FX_OK; 10827c478bd9Sstevel@tonic-gate send_status(id, status); 10837c478bd9Sstevel@tonic-gate xfree(oldpath); 10847c478bd9Sstevel@tonic-gate xfree(newpath); 10857c478bd9Sstevel@tonic-gate } 10867c478bd9Sstevel@tonic-gate 10877c478bd9Sstevel@tonic-gate static void 10887c478bd9Sstevel@tonic-gate process_extended(void) 10897c478bd9Sstevel@tonic-gate { 10907c478bd9Sstevel@tonic-gate u_int32_t id; 10917c478bd9Sstevel@tonic-gate char *request; 10927c478bd9Sstevel@tonic-gate 10937c478bd9Sstevel@tonic-gate id = get_int(); 10947c478bd9Sstevel@tonic-gate request = get_string(NULL); 10957c478bd9Sstevel@tonic-gate send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 10967c478bd9Sstevel@tonic-gate xfree(request); 10977c478bd9Sstevel@tonic-gate } 10987c478bd9Sstevel@tonic-gate 10997c478bd9Sstevel@tonic-gate /* stolen from ssh-agent */ 11007c478bd9Sstevel@tonic-gate 11017c478bd9Sstevel@tonic-gate static void 11027c478bd9Sstevel@tonic-gate process(void) 11037c478bd9Sstevel@tonic-gate { 11047c478bd9Sstevel@tonic-gate u_int msg_len; 11057c478bd9Sstevel@tonic-gate u_int buf_len; 11067c478bd9Sstevel@tonic-gate u_int consumed; 11077c478bd9Sstevel@tonic-gate u_int type; 11087c478bd9Sstevel@tonic-gate u_char *cp; 11097c478bd9Sstevel@tonic-gate 11107c478bd9Sstevel@tonic-gate buf_len = buffer_len(&iqueue); 11117c478bd9Sstevel@tonic-gate if (buf_len < 5) 11127c478bd9Sstevel@tonic-gate return; /* Incomplete message. */ 11137c478bd9Sstevel@tonic-gate cp = buffer_ptr(&iqueue); 1114*90685d2cSjp161948 msg_len = get_u32(cp); 1115*90685d2cSjp161948 if (msg_len > SFTP_MAX_MSG_LENGTH) { 1116*90685d2cSjp161948 error("bad message from %s local user %s", 1117*90685d2cSjp161948 client_addr, pw->pw_name); 1118*90685d2cSjp161948 cleanup_exit(11); 11197c478bd9Sstevel@tonic-gate } 11207c478bd9Sstevel@tonic-gate if (buf_len < msg_len + 4) 11217c478bd9Sstevel@tonic-gate return; 11227c478bd9Sstevel@tonic-gate buffer_consume(&iqueue, 4); 11237c478bd9Sstevel@tonic-gate buf_len -= 4; 11247c478bd9Sstevel@tonic-gate type = buffer_get_char(&iqueue); 11257c478bd9Sstevel@tonic-gate switch (type) { 11267c478bd9Sstevel@tonic-gate case SSH2_FXP_INIT: 11277c478bd9Sstevel@tonic-gate process_init(); 11287c478bd9Sstevel@tonic-gate break; 11297c478bd9Sstevel@tonic-gate case SSH2_FXP_OPEN: 11307c478bd9Sstevel@tonic-gate process_open(); 11317c478bd9Sstevel@tonic-gate break; 11327c478bd9Sstevel@tonic-gate case SSH2_FXP_CLOSE: 11337c478bd9Sstevel@tonic-gate process_close(); 11347c478bd9Sstevel@tonic-gate break; 11357c478bd9Sstevel@tonic-gate case SSH2_FXP_READ: 11367c478bd9Sstevel@tonic-gate process_read(); 11377c478bd9Sstevel@tonic-gate break; 11387c478bd9Sstevel@tonic-gate case SSH2_FXP_WRITE: 11397c478bd9Sstevel@tonic-gate process_write(); 11407c478bd9Sstevel@tonic-gate break; 11417c478bd9Sstevel@tonic-gate case SSH2_FXP_LSTAT: 11427c478bd9Sstevel@tonic-gate process_lstat(); 11437c478bd9Sstevel@tonic-gate break; 11447c478bd9Sstevel@tonic-gate case SSH2_FXP_FSTAT: 11457c478bd9Sstevel@tonic-gate process_fstat(); 11467c478bd9Sstevel@tonic-gate break; 11477c478bd9Sstevel@tonic-gate case SSH2_FXP_SETSTAT: 11487c478bd9Sstevel@tonic-gate process_setstat(); 11497c478bd9Sstevel@tonic-gate break; 11507c478bd9Sstevel@tonic-gate case SSH2_FXP_FSETSTAT: 11517c478bd9Sstevel@tonic-gate process_fsetstat(); 11527c478bd9Sstevel@tonic-gate break; 11537c478bd9Sstevel@tonic-gate case SSH2_FXP_OPENDIR: 11547c478bd9Sstevel@tonic-gate process_opendir(); 11557c478bd9Sstevel@tonic-gate break; 11567c478bd9Sstevel@tonic-gate case SSH2_FXP_READDIR: 11577c478bd9Sstevel@tonic-gate process_readdir(); 11587c478bd9Sstevel@tonic-gate break; 11597c478bd9Sstevel@tonic-gate case SSH2_FXP_REMOVE: 11607c478bd9Sstevel@tonic-gate process_remove(); 11617c478bd9Sstevel@tonic-gate break; 11627c478bd9Sstevel@tonic-gate case SSH2_FXP_MKDIR: 11637c478bd9Sstevel@tonic-gate process_mkdir(); 11647c478bd9Sstevel@tonic-gate break; 11657c478bd9Sstevel@tonic-gate case SSH2_FXP_RMDIR: 11667c478bd9Sstevel@tonic-gate process_rmdir(); 11677c478bd9Sstevel@tonic-gate break; 11687c478bd9Sstevel@tonic-gate case SSH2_FXP_REALPATH: 11697c478bd9Sstevel@tonic-gate process_realpath(); 11707c478bd9Sstevel@tonic-gate break; 11717c478bd9Sstevel@tonic-gate case SSH2_FXP_STAT: 11727c478bd9Sstevel@tonic-gate process_stat(); 11737c478bd9Sstevel@tonic-gate break; 11747c478bd9Sstevel@tonic-gate case SSH2_FXP_RENAME: 11757c478bd9Sstevel@tonic-gate process_rename(); 11767c478bd9Sstevel@tonic-gate break; 11777c478bd9Sstevel@tonic-gate case SSH2_FXP_READLINK: 11787c478bd9Sstevel@tonic-gate process_readlink(); 11797c478bd9Sstevel@tonic-gate break; 11807c478bd9Sstevel@tonic-gate case SSH2_FXP_SYMLINK: 11817c478bd9Sstevel@tonic-gate process_symlink(); 11827c478bd9Sstevel@tonic-gate break; 11837c478bd9Sstevel@tonic-gate case SSH2_FXP_EXTENDED: 11847c478bd9Sstevel@tonic-gate process_extended(); 11857c478bd9Sstevel@tonic-gate break; 11867c478bd9Sstevel@tonic-gate default: 11877c478bd9Sstevel@tonic-gate error("Unknown message %d", type); 11887c478bd9Sstevel@tonic-gate break; 11897c478bd9Sstevel@tonic-gate } 11907c478bd9Sstevel@tonic-gate /* discard the remaining bytes from the current packet */ 11917c478bd9Sstevel@tonic-gate if (buf_len < buffer_len(&iqueue)) 1192*90685d2cSjp161948 fatal("iqueue grew unexpectedly"); 11937c478bd9Sstevel@tonic-gate consumed = buf_len - buffer_len(&iqueue); 11947c478bd9Sstevel@tonic-gate if (msg_len < consumed) 11957c478bd9Sstevel@tonic-gate fatal("msg_len %d < consumed %d", msg_len, consumed); 11967c478bd9Sstevel@tonic-gate if (msg_len > consumed) 11977c478bd9Sstevel@tonic-gate buffer_consume(&iqueue, msg_len - consumed); 11987c478bd9Sstevel@tonic-gate } 11997c478bd9Sstevel@tonic-gate 1200*90685d2cSjp161948 /* Cleanup handler that logs active handles upon normal exit */ 1201*90685d2cSjp161948 static void 1202*90685d2cSjp161948 cleanup_exit(int i) 1203*90685d2cSjp161948 { 1204*90685d2cSjp161948 if (pw != NULL && client_addr != NULL) { 1205*90685d2cSjp161948 handle_log_exit(); 1206*90685d2cSjp161948 log("session closed for local user %s from [%s]", 1207*90685d2cSjp161948 pw->pw_name, client_addr); 1208*90685d2cSjp161948 } 1209*90685d2cSjp161948 _exit(i); 1210*90685d2cSjp161948 } 1211*90685d2cSjp161948 1212*90685d2cSjp161948 static void 1213*90685d2cSjp161948 usage(void) 1214*90685d2cSjp161948 { 1215*90685d2cSjp161948 fprintf(stderr, 1216*90685d2cSjp161948 "Usage: %s [-he] [-l log_level] [-f log_facility]\n", __progname); 1217*90685d2cSjp161948 exit(1); 1218*90685d2cSjp161948 } 1219*90685d2cSjp161948 12207c478bd9Sstevel@tonic-gate int 1221*90685d2cSjp161948 main(int argc, char **argv) 12227c478bd9Sstevel@tonic-gate { 12237c478bd9Sstevel@tonic-gate fd_set *rset, *wset; 1224*90685d2cSjp161948 int in, out, max, ch, skipargs = 0, log_stderr = 0; 12257c478bd9Sstevel@tonic-gate ssize_t len, olen, set_size; 1226*90685d2cSjp161948 SyslogFacility log_facility = SYSLOG_FACILITY_AUTH; 1227*90685d2cSjp161948 char *cp, buf[4*4096]; 12287c478bd9Sstevel@tonic-gate 1229*90685d2cSjp161948 extern char *optarg; 12307c478bd9Sstevel@tonic-gate 1231*90685d2cSjp161948 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ 1232*90685d2cSjp161948 sanitise_stdfd(); 1233*90685d2cSjp161948 1234*90685d2cSjp161948 __progname = get_progname(argv[0]); 12357c478bd9Sstevel@tonic-gate 12367c478bd9Sstevel@tonic-gate (void) g11n_setlocale(LC_ALL, ""); 12377c478bd9Sstevel@tonic-gate 1238*90685d2cSjp161948 log_init(__progname, log_level, log_facility, log_stderr); 12397c478bd9Sstevel@tonic-gate 1240*90685d2cSjp161948 while (!skipargs && (ch = getopt(argc, argv, "C:f:l:che")) != -1) { 1241*90685d2cSjp161948 switch (ch) { 1242*90685d2cSjp161948 case 'c': 1243*90685d2cSjp161948 /* 1244*90685d2cSjp161948 * Ignore all arguments if we are invoked as a 1245*90685d2cSjp161948 * shell using "sftp-server -c command" 1246*90685d2cSjp161948 */ 1247*90685d2cSjp161948 skipargs = 1; 1248*90685d2cSjp161948 break; 1249*90685d2cSjp161948 case 'e': 1250*90685d2cSjp161948 log_stderr = 1; 1251*90685d2cSjp161948 break; 1252*90685d2cSjp161948 case 'l': 1253*90685d2cSjp161948 log_level = log_level_number(optarg); 1254*90685d2cSjp161948 if (log_level == SYSLOG_LEVEL_NOT_SET) 1255*90685d2cSjp161948 error("Invalid log level \"%s\"", optarg); 1256*90685d2cSjp161948 break; 1257*90685d2cSjp161948 case 'f': 1258*90685d2cSjp161948 log_facility = log_facility_number(optarg); 1259*90685d2cSjp161948 if (log_facility == SYSLOG_FACILITY_NOT_SET) 1260*90685d2cSjp161948 error("Invalid log facility \"%s\"", optarg); 1261*90685d2cSjp161948 break; 1262*90685d2cSjp161948 case 'h': 1263*90685d2cSjp161948 default: 1264*90685d2cSjp161948 usage(); 1265*90685d2cSjp161948 } 1266*90685d2cSjp161948 } 1267*90685d2cSjp161948 1268*90685d2cSjp161948 log_init(__progname, log_level, log_facility, log_stderr); 1269*90685d2cSjp161948 1270*90685d2cSjp161948 if ((cp = getenv("SSH_CONNECTION")) != NULL) { 1271*90685d2cSjp161948 client_addr = xstrdup(cp); 1272*90685d2cSjp161948 if ((cp = strchr(client_addr, ' ')) == NULL) 1273*90685d2cSjp161948 fatal("Malformed SSH_CONNECTION variable: \"%s\"", 1274*90685d2cSjp161948 getenv("SSH_CONNECTION")); 1275*90685d2cSjp161948 *cp = '\0'; 1276*90685d2cSjp161948 } else 1277*90685d2cSjp161948 client_addr = xstrdup("UNKNOWN"); 1278*90685d2cSjp161948 1279*90685d2cSjp161948 if ((pw = getpwuid(getuid())) == NULL) 1280*90685d2cSjp161948 fatal("No user found for uid %lu", (u_long)getuid()); 1281*90685d2cSjp161948 pw = pwcopy(pw); 1282*90685d2cSjp161948 1283*90685d2cSjp161948 log("session opened for local user %s from [%s]", 1284*90685d2cSjp161948 pw->pw_name, client_addr); 1285*90685d2cSjp161948 1286*90685d2cSjp161948 handle_init(); 12877c478bd9Sstevel@tonic-gate 12887c478bd9Sstevel@tonic-gate in = dup(STDIN_FILENO); 12897c478bd9Sstevel@tonic-gate out = dup(STDOUT_FILENO); 12907c478bd9Sstevel@tonic-gate 12917c478bd9Sstevel@tonic-gate #ifdef HAVE_CYGWIN 12927c478bd9Sstevel@tonic-gate setmode(in, O_BINARY); 12937c478bd9Sstevel@tonic-gate setmode(out, O_BINARY); 12947c478bd9Sstevel@tonic-gate #endif 12957c478bd9Sstevel@tonic-gate 12967c478bd9Sstevel@tonic-gate max = 0; 12977c478bd9Sstevel@tonic-gate if (in > max) 12987c478bd9Sstevel@tonic-gate max = in; 12997c478bd9Sstevel@tonic-gate if (out > max) 13007c478bd9Sstevel@tonic-gate max = out; 13017c478bd9Sstevel@tonic-gate 13027c478bd9Sstevel@tonic-gate buffer_init(&iqueue); 13037c478bd9Sstevel@tonic-gate buffer_init(&oqueue); 13047c478bd9Sstevel@tonic-gate 13057c478bd9Sstevel@tonic-gate set_size = howmany(max + 1, NFDBITS) * sizeof(fd_mask); 13067c478bd9Sstevel@tonic-gate rset = (fd_set *)xmalloc(set_size); 13077c478bd9Sstevel@tonic-gate wset = (fd_set *)xmalloc(set_size); 13087c478bd9Sstevel@tonic-gate 13097c478bd9Sstevel@tonic-gate for (;;) { 13107c478bd9Sstevel@tonic-gate memset(rset, 0, set_size); 13117c478bd9Sstevel@tonic-gate memset(wset, 0, set_size); 13127c478bd9Sstevel@tonic-gate 1313*90685d2cSjp161948 /* 1314*90685d2cSjp161948 * Ensure that we can read a full buffer and handle 1315*90685d2cSjp161948 * the worst-case length packet it can generate, 1316*90685d2cSjp161948 * otherwise apply backpressure by stopping reads. 1317*90685d2cSjp161948 */ 1318*90685d2cSjp161948 if (buffer_check_alloc(&iqueue, sizeof(buf)) && 1319*90685d2cSjp161948 buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) 13207c478bd9Sstevel@tonic-gate FD_SET(in, rset); 1321*90685d2cSjp161948 13227c478bd9Sstevel@tonic-gate olen = buffer_len(&oqueue); 13237c478bd9Sstevel@tonic-gate if (olen > 0) 13247c478bd9Sstevel@tonic-gate FD_SET(out, wset); 13257c478bd9Sstevel@tonic-gate 13267c478bd9Sstevel@tonic-gate if (select(max+1, rset, wset, NULL, NULL) < 0) { 13277c478bd9Sstevel@tonic-gate if (errno == EINTR) 13287c478bd9Sstevel@tonic-gate continue; 1329*90685d2cSjp161948 error("select: %s", strerror(errno)); 1330*90685d2cSjp161948 cleanup_exit(2); 13317c478bd9Sstevel@tonic-gate } 13327c478bd9Sstevel@tonic-gate 13337c478bd9Sstevel@tonic-gate /* copy stdin to iqueue */ 13347c478bd9Sstevel@tonic-gate if (FD_ISSET(in, rset)) { 13357c478bd9Sstevel@tonic-gate len = read(in, buf, sizeof buf); 13367c478bd9Sstevel@tonic-gate if (len == 0) { 13377c478bd9Sstevel@tonic-gate debug("read eof"); 1338*90685d2cSjp161948 cleanup_exit(0); 13397c478bd9Sstevel@tonic-gate } else if (len < 0) { 1340*90685d2cSjp161948 error("read: %s", strerror(errno)); 1341*90685d2cSjp161948 cleanup_exit(1); 13427c478bd9Sstevel@tonic-gate } else { 13437c478bd9Sstevel@tonic-gate buffer_append(&iqueue, buf, len); 13447c478bd9Sstevel@tonic-gate } 13457c478bd9Sstevel@tonic-gate } 13467c478bd9Sstevel@tonic-gate /* send oqueue to stdout */ 13477c478bd9Sstevel@tonic-gate if (FD_ISSET(out, wset)) { 13487c478bd9Sstevel@tonic-gate len = write(out, buffer_ptr(&oqueue), olen); 13497c478bd9Sstevel@tonic-gate if (len < 0) { 1350*90685d2cSjp161948 error("write: %s", strerror(errno)); 1351*90685d2cSjp161948 cleanup_exit(1); 13527c478bd9Sstevel@tonic-gate } else { 13537c478bd9Sstevel@tonic-gate buffer_consume(&oqueue, len); 13547c478bd9Sstevel@tonic-gate } 13557c478bd9Sstevel@tonic-gate } 1356*90685d2cSjp161948 1357*90685d2cSjp161948 /* 1358*90685d2cSjp161948 * Process requests from client if we can fit the results 1359*90685d2cSjp161948 * into the output buffer, otherwise stop processing input 1360*90685d2cSjp161948 * and let the output queue drain. 1361*90685d2cSjp161948 */ 1362*90685d2cSjp161948 if (buffer_check_alloc(&oqueue, SFTP_MAX_MSG_LENGTH)) 13637c478bd9Sstevel@tonic-gate process(); 13647c478bd9Sstevel@tonic-gate } 1365*90685d2cSjp161948 1366*90685d2cSjp161948 /* NOTREACHED */ 1367*90685d2cSjp161948 return (0); 13687c478bd9Sstevel@tonic-gate } 1369