summaryrefslogtreecommitdiff
path: root/sftp-server.c
diff options
context:
space:
mode:
Diffstat (limited to 'sftp-server.c')
-rw-r--r--sftp-server.c220
1 files changed, 167 insertions, 53 deletions
diff --git a/sftp-server.c b/sftp-server.c
index 76edebc5a..24c4ff717 100644
--- a/sftp-server.c
+++ b/sftp-server.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-server.c,v 1.73 2007/05/17 07:55:29 djm Exp $ */ 1/* $OpenBSD: sftp-server.c,v 1.84 2008/06/26 06:10:09 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000-2004 Markus Friedl. All rights reserved. 3 * Copyright (c) 2000-2004 Markus Friedl. All rights reserved.
4 * 4 *
@@ -23,6 +23,12 @@
23#ifdef HAVE_SYS_TIME_H 23#ifdef HAVE_SYS_TIME_H
24# include <sys/time.h> 24# include <sys/time.h>
25#endif 25#endif
26#ifdef HAVE_SYS_MOUNT_H
27#include <sys/mount.h>
28#endif
29#ifdef HAVE_SYS_STATVFS_H
30#include <sys/statvfs.h>
31#endif
26 32
27#include <dirent.h> 33#include <dirent.h>
28#include <errno.h> 34#include <errno.h>
@@ -98,6 +104,9 @@ errno_to_portable(int unixerrno)
98 case EINVAL: 104 case EINVAL:
99 ret = SSH2_FX_BAD_MESSAGE; 105 ret = SSH2_FX_BAD_MESSAGE;
100 break; 106 break;
107 case ENOSYS:
108 ret = SSH2_FX_OP_UNSUPPORTED;
109 break;
101 default: 110 default:
102 ret = SSH2_FX_FAILURE; 111 ret = SSH2_FX_FAILURE;
103 break; 112 break;
@@ -169,6 +178,7 @@ struct Handle {
169 int fd; 178 int fd;
170 char *name; 179 char *name;
171 u_int64_t bytes_read, bytes_write; 180 u_int64_t bytes_read, bytes_write;
181 int next_unused;
172}; 182};
173 183
174enum { 184enum {
@@ -177,40 +187,46 @@ enum {
177 HANDLE_FILE 187 HANDLE_FILE
178}; 188};
179 189
180Handle handles[100]; 190Handle *handles = NULL;
191u_int num_handles = 0;
192int first_unused_handle = -1;
181 193
182static void 194static void handle_unused(int i)
183handle_init(void)
184{ 195{
185 u_int i; 196 handles[i].use = HANDLE_UNUSED;
186 197 handles[i].next_unused = first_unused_handle;
187 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) 198 first_unused_handle = i;
188 handles[i].use = HANDLE_UNUSED;
189} 199}
190 200
191static int 201static int
192handle_new(int use, const char *name, int fd, DIR *dirp) 202handle_new(int use, const char *name, int fd, DIR *dirp)
193{ 203{
194 u_int i; 204 int i;
195 205
196 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) { 206 if (first_unused_handle == -1) {
197 if (handles[i].use == HANDLE_UNUSED) { 207 if (num_handles + 1 <= num_handles)
198 handles[i].use = use; 208 return -1;
199 handles[i].dirp = dirp; 209 num_handles++;
200 handles[i].fd = fd; 210 handles = xrealloc(handles, num_handles, sizeof(Handle));
201 handles[i].name = xstrdup(name); 211 handle_unused(num_handles - 1);
202 handles[i].bytes_read = handles[i].bytes_write = 0;
203 return i;
204 }
205 } 212 }
206 return -1; 213
214 i = first_unused_handle;
215 first_unused_handle = handles[i].next_unused;
216
217 handles[i].use = use;
218 handles[i].dirp = dirp;
219 handles[i].fd = fd;
220 handles[i].name = xstrdup(name);
221 handles[i].bytes_read = handles[i].bytes_write = 0;
222
223 return i;
207} 224}
208 225
209static int 226static int
210handle_is_ok(int i, int type) 227handle_is_ok(int i, int type)
211{ 228{
212 return i >= 0 && (u_int)i < sizeof(handles)/sizeof(Handle) && 229 return i >= 0 && (u_int)i < num_handles && handles[i].use == type;
213 handles[i].use == type;
214} 230}
215 231
216static int 232static int
@@ -300,12 +316,12 @@ handle_close(int handle)
300 316
301 if (handle_is_ok(handle, HANDLE_FILE)) { 317 if (handle_is_ok(handle, HANDLE_FILE)) {
302 ret = close(handles[handle].fd); 318 ret = close(handles[handle].fd);
303 handles[handle].use = HANDLE_UNUSED;
304 xfree(handles[handle].name); 319 xfree(handles[handle].name);
320 handle_unused(handle);
305 } else if (handle_is_ok(handle, HANDLE_DIR)) { 321 } else if (handle_is_ok(handle, HANDLE_DIR)) {
306 ret = closedir(handles[handle].dirp); 322 ret = closedir(handles[handle].dirp);
307 handles[handle].use = HANDLE_UNUSED;
308 xfree(handles[handle].name); 323 xfree(handles[handle].name);
324 handle_unused(handle);
309 } else { 325 } else {
310 errno = ENOENT; 326 errno = ENOENT;
311 } 327 }
@@ -333,7 +349,7 @@ handle_log_exit(void)
333{ 349{
334 u_int i; 350 u_int i;
335 351
336 for (i = 0; i < sizeof(handles)/sizeof(Handle); i++) 352 for (i = 0; i < num_handles; i++)
337 if (handles[i].use != HANDLE_UNUSED) 353 if (handles[i].use != HANDLE_UNUSED)
338 handle_log_close(i, "forced"); 354 handle_log_close(i, "forced");
339} 355}
@@ -468,6 +484,33 @@ send_attrib(u_int32_t id, const Attrib *a)
468 buffer_free(&msg); 484 buffer_free(&msg);
469} 485}
470 486
487static void
488send_statvfs(u_int32_t id, struct statvfs *st)
489{
490 Buffer msg;
491 u_int64_t flag;
492
493 flag = (st->f_flag & ST_RDONLY) ? SSH2_FXE_STATVFS_ST_RDONLY : 0;
494 flag |= (st->f_flag & ST_NOSUID) ? SSH2_FXE_STATVFS_ST_NOSUID : 0;
495
496 buffer_init(&msg);
497 buffer_put_char(&msg, SSH2_FXP_EXTENDED_REPLY);
498 buffer_put_int(&msg, id);
499 buffer_put_int64(&msg, st->f_bsize);
500 buffer_put_int64(&msg, st->f_frsize);
501 buffer_put_int64(&msg, st->f_blocks);
502 buffer_put_int64(&msg, st->f_bfree);
503 buffer_put_int64(&msg, st->f_bavail);
504 buffer_put_int64(&msg, st->f_files);
505 buffer_put_int64(&msg, st->f_ffree);
506 buffer_put_int64(&msg, st->f_favail);
507 buffer_put_int64(&msg, FSID_TO_ULONG(st->f_fsid));
508 buffer_put_int64(&msg, flag);
509 buffer_put_int64(&msg, st->f_namemax);
510 send_msg(&msg);
511 buffer_free(&msg);
512}
513
471/* parse incoming */ 514/* parse incoming */
472 515
473static void 516static void
@@ -480,6 +523,15 @@ process_init(void)
480 buffer_init(&msg); 523 buffer_init(&msg);
481 buffer_put_char(&msg, SSH2_FXP_VERSION); 524 buffer_put_char(&msg, SSH2_FXP_VERSION);
482 buffer_put_int(&msg, SSH2_FILEXFER_VERSION); 525 buffer_put_int(&msg, SSH2_FILEXFER_VERSION);
526 /* POSIX rename extension */
527 buffer_put_cstring(&msg, "posix-rename@openssh.com");
528 buffer_put_cstring(&msg, "1"); /* version */
529 /* statvfs extension */
530 buffer_put_cstring(&msg, "statvfs@openssh.com");
531 buffer_put_cstring(&msg, "2"); /* version */
532 /* fstatvfs extension */
533 buffer_put_cstring(&msg, "fstatvfs@openssh.com");
534 buffer_put_cstring(&msg, "2"); /* version */
483 send_msg(&msg); 535 send_msg(&msg);
484 buffer_free(&msg); 536 buffer_free(&msg);
485} 537}
@@ -711,7 +763,7 @@ process_setstat(void)
711 } 763 }
712 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 764 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
713 logit("set \"%s\" mode %04o", name, a->perm); 765 logit("set \"%s\" mode %04o", name, a->perm);
714 ret = chmod(name, a->perm & 0777); 766 ret = chmod(name, a->perm & 07777);
715 if (ret == -1) 767 if (ret == -1)
716 status = errno_to_portable(errno); 768 status = errno_to_portable(errno);
717 } 769 }
@@ -765,9 +817,9 @@ process_fsetstat(void)
765 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { 817 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) {
766 logit("set \"%s\" mode %04o", name, a->perm); 818 logit("set \"%s\" mode %04o", name, a->perm);
767#ifdef HAVE_FCHMOD 819#ifdef HAVE_FCHMOD
768 ret = fchmod(fd, a->perm & 0777); 820 ret = fchmod(fd, a->perm & 07777);
769#else 821#else
770 ret = chmod(name, a->perm & 0777); 822 ret = chmod(name, a->perm & 07777);
771#endif 823#endif
772 if (ret == -1) 824 if (ret == -1)
773 status = errno_to_portable(errno); 825 status = errno_to_portable(errno);
@@ -918,7 +970,7 @@ process_mkdir(void)
918 name = get_string(NULL); 970 name = get_string(NULL);
919 a = get_attrib(); 971 a = get_attrib();
920 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ? 972 mode = (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) ?
921 a->perm & 0777 : 0777; 973 a->perm & 07777 : 0777;
922 debug3("request %u: mkdir", id); 974 debug3("request %u: mkdir", id);
923 logit("mkdir name \"%s\" mode 0%o", name, mode); 975 logit("mkdir name \"%s\" mode 0%o", name, mode);
924 ret = mkdir(name, mode); 976 ret = mkdir(name, mode);
@@ -990,6 +1042,9 @@ process_rename(void)
990 /* Race-free rename of regular files */ 1042 /* Race-free rename of regular files */
991 if (link(oldpath, newpath) == -1) { 1043 if (link(oldpath, newpath) == -1) {
992 if (errno == EOPNOTSUPP 1044 if (errno == EOPNOTSUPP
1045#ifdef EXDEV
1046 || errno == EXDEV
1047#endif
993#ifdef LINK_OPNOTSUPP_ERRNO 1048#ifdef LINK_OPNOTSUPP_ERRNO
994 || errno == LINK_OPNOTSUPP_ERRNO 1049 || errno == LINK_OPNOTSUPP_ERRNO
995#endif 1050#endif
@@ -1073,6 +1128,59 @@ process_symlink(void)
1073} 1128}
1074 1129
1075static void 1130static void
1131process_extended_posix_rename(u_int32_t id)
1132{
1133 char *oldpath, *newpath;
1134
1135 oldpath = get_string(NULL);
1136 newpath = get_string(NULL);
1137 debug3("request %u: posix-rename", id);
1138 logit("posix-rename old \"%s\" new \"%s\"", oldpath, newpath);
1139 if (rename(oldpath, newpath) == -1)
1140 send_status(id, errno_to_portable(errno));
1141 else
1142 send_status(id, SSH2_FX_OK);
1143 xfree(oldpath);
1144 xfree(newpath);
1145}
1146
1147static void
1148process_extended_statvfs(u_int32_t id)
1149{
1150 char *path;
1151 struct statvfs st;
1152
1153 path = get_string(NULL);
1154 debug3("request %u: statfs", id);
1155 logit("statfs \"%s\"", path);
1156
1157 if (statvfs(path, &st) != 0)
1158 send_status(id, errno_to_portable(errno));
1159 else
1160 send_statvfs(id, &st);
1161 xfree(path);
1162}
1163
1164static void
1165process_extended_fstatvfs(u_int32_t id)
1166{
1167 int handle, fd;
1168 struct statvfs st;
1169
1170 handle = get_handle();
1171 debug("request %u: fstatvfs \"%s\" (handle %u)",
1172 id, handle_to_name(handle), handle);
1173 if ((fd = handle_to_fd(handle)) < 0) {
1174 send_status(id, SSH2_FX_FAILURE);
1175 return;
1176 }
1177 if (fstatvfs(fd, &st) != 0)
1178 send_status(id, errno_to_portable(errno));
1179 else
1180 send_statvfs(id, &st);
1181}
1182
1183static void
1076process_extended(void) 1184process_extended(void)
1077{ 1185{
1078 u_int32_t id; 1186 u_int32_t id;
@@ -1080,7 +1188,14 @@ process_extended(void)
1080 1188
1081 id = get_int(); 1189 id = get_int();
1082 request = get_string(NULL); 1190 request = get_string(NULL);
1083 send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */ 1191 if (strcmp(request, "posix-rename@openssh.com") == 0)
1192 process_extended_posix_rename(id);
1193 else if (strcmp(request, "statvfs@openssh.com") == 0)
1194 process_extended_statvfs(id);
1195 else if (strcmp(request, "fstatvfs@openssh.com") == 0)
1196 process_extended_fstatvfs(id);
1197 else
1198 send_status(id, SSH2_FX_OP_UNSUPPORTED); /* MUST */
1084 xfree(request); 1199 xfree(request);
1085} 1200}
1086 1201
@@ -1103,7 +1218,7 @@ process(void)
1103 if (msg_len > SFTP_MAX_MSG_LENGTH) { 1218 if (msg_len > SFTP_MAX_MSG_LENGTH) {
1104 error("bad message from %s local user %s", 1219 error("bad message from %s local user %s",
1105 client_addr, pw->pw_name); 1220 client_addr, pw->pw_name);
1106 cleanup_exit(11); 1221 sftp_server_cleanup_exit(11);
1107 } 1222 }
1108 if (buf_len < msg_len + 4) 1223 if (buf_len < msg_len + 4)
1109 return; 1224 return;
@@ -1176,18 +1291,22 @@ process(void)
1176 break; 1291 break;
1177 } 1292 }
1178 /* discard the remaining bytes from the current packet */ 1293 /* discard the remaining bytes from the current packet */
1179 if (buf_len < buffer_len(&iqueue)) 1294 if (buf_len < buffer_len(&iqueue)) {
1180 fatal("iqueue grew unexpectedly"); 1295 error("iqueue grew unexpectedly");
1296 sftp_server_cleanup_exit(255);
1297 }
1181 consumed = buf_len - buffer_len(&iqueue); 1298 consumed = buf_len - buffer_len(&iqueue);
1182 if (msg_len < consumed) 1299 if (msg_len < consumed) {
1183 fatal("msg_len %d < consumed %d", msg_len, consumed); 1300 error("msg_len %d < consumed %d", msg_len, consumed);
1301 sftp_server_cleanup_exit(255);
1302 }
1184 if (msg_len > consumed) 1303 if (msg_len > consumed)
1185 buffer_consume(&iqueue, msg_len - consumed); 1304 buffer_consume(&iqueue, msg_len - consumed);
1186} 1305}
1187 1306
1188/* Cleanup handler that logs active handles upon normal exit */ 1307/* Cleanup handler that logs active handles upon normal exit */
1189void 1308void
1190cleanup_exit(int i) 1309sftp_server_cleanup_exit(int i)
1191{ 1310{
1192 if (pw != NULL && client_addr != NULL) { 1311 if (pw != NULL && client_addr != NULL) {
1193 handle_log_exit(); 1312 handle_log_exit();
@@ -1198,7 +1317,7 @@ cleanup_exit(int i)
1198} 1317}
1199 1318
1200static void 1319static void
1201usage(void) 1320sftp_server_usage(void)
1202{ 1321{
1203 extern char *__progname; 1322 extern char *__progname;
1204 1323
@@ -1208,7 +1327,7 @@ usage(void)
1208} 1327}
1209 1328
1210int 1329int
1211main(int argc, char **argv) 1330sftp_server_main(int argc, char **argv, struct passwd *user_pw)
1212{ 1331{
1213 fd_set *rset, *wset; 1332 fd_set *rset, *wset;
1214 int in, out, max, ch, skipargs = 0, log_stderr = 0; 1333 int in, out, max, ch, skipargs = 0, log_stderr = 0;
@@ -1219,9 +1338,6 @@ main(int argc, char **argv)
1219 extern char *optarg; 1338 extern char *optarg;
1220 extern char *__progname; 1339 extern char *__progname;
1221 1340
1222 /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */
1223 sanitise_stdfd();
1224
1225 __progname = ssh_get_progname(argv[0]); 1341 __progname = ssh_get_progname(argv[0]);
1226 log_init(__progname, log_level, log_facility, log_stderr); 1342 log_init(__progname, log_level, log_facility, log_stderr);
1227 1343
@@ -1244,12 +1360,12 @@ main(int argc, char **argv)
1244 break; 1360 break;
1245 case 'f': 1361 case 'f':
1246 log_facility = log_facility_number(optarg); 1362 log_facility = log_facility_number(optarg);
1247 if (log_level == SYSLOG_FACILITY_NOT_SET) 1363 if (log_facility == SYSLOG_FACILITY_NOT_SET)
1248 error("Invalid log facility \"%s\"", optarg); 1364 error("Invalid log facility \"%s\"", optarg);
1249 break; 1365 break;
1250 case 'h': 1366 case 'h':
1251 default: 1367 default:
1252 usage(); 1368 sftp_server_usage();
1253 } 1369 }
1254 } 1370 }
1255 1371
@@ -1257,22 +1373,20 @@ main(int argc, char **argv)
1257 1373
1258 if ((cp = getenv("SSH_CONNECTION")) != NULL) { 1374 if ((cp = getenv("SSH_CONNECTION")) != NULL) {
1259 client_addr = xstrdup(cp); 1375 client_addr = xstrdup(cp);
1260 if ((cp = strchr(client_addr, ' ')) == NULL) 1376 if ((cp = strchr(client_addr, ' ')) == NULL) {
1261 fatal("Malformed SSH_CONNECTION variable: \"%s\"", 1377 error("Malformed SSH_CONNECTION variable: \"%s\"",
1262 getenv("SSH_CONNECTION")); 1378 getenv("SSH_CONNECTION"));
1379 sftp_server_cleanup_exit(255);
1380 }
1263 *cp = '\0'; 1381 *cp = '\0';
1264 } else 1382 } else
1265 client_addr = xstrdup("UNKNOWN"); 1383 client_addr = xstrdup("UNKNOWN");
1266 1384
1267 if ((pw = getpwuid(getuid())) == NULL) 1385 pw = pwcopy(user_pw);
1268 fatal("No user found for uid %lu", (u_long)getuid());
1269 pw = pwcopy(pw);
1270 1386
1271 logit("session opened for local user %s from [%s]", 1387 logit("session opened for local user %s from [%s]",
1272 pw->pw_name, client_addr); 1388 pw->pw_name, client_addr);
1273 1389
1274 handle_init();
1275
1276 in = dup(STDIN_FILENO); 1390 in = dup(STDIN_FILENO);
1277 out = dup(STDOUT_FILENO); 1391 out = dup(STDOUT_FILENO);
1278 1392
@@ -1315,7 +1429,7 @@ main(int argc, char **argv)
1315 if (errno == EINTR) 1429 if (errno == EINTR)
1316 continue; 1430 continue;
1317 error("select: %s", strerror(errno)); 1431 error("select: %s", strerror(errno));
1318 cleanup_exit(2); 1432 sftp_server_cleanup_exit(2);
1319 } 1433 }
1320 1434
1321 /* copy stdin to iqueue */ 1435 /* copy stdin to iqueue */
@@ -1323,10 +1437,10 @@ main(int argc, char **argv)
1323 len = read(in, buf, sizeof buf); 1437 len = read(in, buf, sizeof buf);
1324 if (len == 0) { 1438 if (len == 0) {
1325 debug("read eof"); 1439 debug("read eof");
1326 cleanup_exit(0); 1440 sftp_server_cleanup_exit(0);
1327 } else if (len < 0) { 1441 } else if (len < 0) {
1328 error("read: %s", strerror(errno)); 1442 error("read: %s", strerror(errno));
1329 cleanup_exit(1); 1443 sftp_server_cleanup_exit(1);
1330 } else { 1444 } else {
1331 buffer_append(&iqueue, buf, len); 1445 buffer_append(&iqueue, buf, len);
1332 } 1446 }
@@ -1336,7 +1450,7 @@ main(int argc, char **argv)
1336 len = write(out, buffer_ptr(&oqueue), olen); 1450 len = write(out, buffer_ptr(&oqueue), olen);
1337 if (len < 0) { 1451 if (len < 0) {
1338 error("write: %s", strerror(errno)); 1452 error("write: %s", strerror(errno));
1339 cleanup_exit(1); 1453 sftp_server_cleanup_exit(1);
1340 } else { 1454 } else {
1341 buffer_consume(&oqueue, len); 1455 buffer_consume(&oqueue, len);
1342 } 1456 }