summaryrefslogtreecommitdiff
path: root/sftp-client.c
diff options
context:
space:
mode:
Diffstat (limited to 'sftp-client.c')
-rw-r--r--sftp-client.c301
1 files changed, 281 insertions, 20 deletions
diff --git a/sftp-client.c b/sftp-client.c
index 0990b7912..6124c0f40 100644
--- a/sftp-client.c
+++ b/sftp-client.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-client.c,v 1.87 2009/06/22 05:39:28 dtucker Exp $ */ 1/* $OpenBSD: sftp-client.c,v 1.90 2009/10/11 10:41:26 dtucker Exp $ */
2/* 2/*
3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org> 3 * Copyright (c) 2001-2004 Damien Miller <djm@openbsd.org>
4 * 4 *
@@ -36,6 +36,7 @@
36#endif 36#endif
37#include <sys/uio.h> 37#include <sys/uio.h>
38 38
39#include <dirent.h>
39#include <errno.h> 40#include <errno.h>
40#include <fcntl.h> 41#include <fcntl.h>
41#include <signal.h> 42#include <signal.h>
@@ -61,6 +62,9 @@ extern int showprogress;
61/* Minimum amount of data to read at a time */ 62/* Minimum amount of data to read at a time */
62#define MIN_READ_SIZE 512 63#define MIN_READ_SIZE 512
63 64
65/* Maximum depth to descend in directory trees */
66#define MAX_DIR_DEPTH 64
67
64struct sftp_conn { 68struct sftp_conn {
65 int fd_in; 69 int fd_in;
66 int fd_out; 70 int fd_out;
@@ -74,6 +78,10 @@ struct sftp_conn {
74 u_int exts; 78 u_int exts;
75}; 79};
76 80
81static char *
82get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...)
83 __attribute__((format(printf, 4, 5)));
84
77static void 85static void
78send_msg(int fd, Buffer *m) 86send_msg(int fd, Buffer *m)
79{ 87{
@@ -179,11 +187,18 @@ get_status(int fd, u_int expected_id)
179} 187}
180 188
181static char * 189static char *
182get_handle(int fd, u_int expected_id, u_int *len) 190get_handle(int fd, u_int expected_id, u_int *len, const char *errfmt, ...)
183{ 191{
184 Buffer msg; 192 Buffer msg;
185 u_int type, id; 193 u_int type, id;
186 char *handle; 194 char *handle, errmsg[256];
195 va_list args;
196 int status;
197
198 va_start(args, errfmt);
199 if (errfmt != NULL)
200 vsnprintf(errmsg, sizeof(errmsg), errfmt, args);
201 va_end(args);
187 202
188 buffer_init(&msg); 203 buffer_init(&msg);
189 get_msg(fd, &msg); 204 get_msg(fd, &msg);
@@ -191,16 +206,17 @@ get_handle(int fd, u_int expected_id, u_int *len)
191 id = buffer_get_int(&msg); 206 id = buffer_get_int(&msg);
192 207
193 if (id != expected_id) 208 if (id != expected_id)
194 fatal("ID mismatch (%u != %u)", id, expected_id); 209 fatal("%s: ID mismatch (%u != %u)",
210 errfmt == NULL ? __func__ : errmsg, id, expected_id);
195 if (type == SSH2_FXP_STATUS) { 211 if (type == SSH2_FXP_STATUS) {
196 int status = buffer_get_int(&msg); 212 status = buffer_get_int(&msg);
197 213 if (errfmt != NULL)
198 error("Couldn't get handle: %s", fx2txt(status)); 214 error("%s: %s", errmsg, fx2txt(status));
199 buffer_free(&msg); 215 buffer_free(&msg);
200 return(NULL); 216 return(NULL);
201 } else if (type != SSH2_FXP_HANDLE) 217 } else if (type != SSH2_FXP_HANDLE)
202 fatal("Expected SSH2_FXP_HANDLE(%u) packet, got %u", 218 fatal("%s: Expected SSH2_FXP_HANDLE(%u) packet, got %u",
203 SSH2_FXP_HANDLE, type); 219 errfmt == NULL ? __func__ : errmsg, SSH2_FXP_HANDLE, type);
204 220
205 handle = buffer_get_string(&msg, len); 221 handle = buffer_get_string(&msg, len);
206 buffer_free(&msg); 222 buffer_free(&msg);
@@ -418,7 +434,8 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
418 434
419 buffer_clear(&msg); 435 buffer_clear(&msg);
420 436
421 handle = get_handle(conn->fd_in, id, &handle_len); 437 handle = get_handle(conn->fd_in, id, &handle_len,
438 "remote readdir(\"%s\")", path);
422 if (handle == NULL) 439 if (handle == NULL)
423 return(-1); 440 return(-1);
424 441
@@ -484,6 +501,17 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
484 if (printflag) 501 if (printflag)
485 printf("%s\n", longname); 502 printf("%s\n", longname);
486 503
504 /*
505 * Directory entries should never contain '/'
506 * These can be used to attack recursive ops
507 * (e.g. send '../../../../etc/passwd')
508 */
509 if (strchr(filename, '/') != NULL) {
510 error("Server sent suspect path \"%s\" "
511 "during readdir of \"%s\"", filename, path);
512 goto next;
513 }
514
487 if (dir) { 515 if (dir) {
488 *dir = xrealloc(*dir, ents + 2, sizeof(**dir)); 516 *dir = xrealloc(*dir, ents + 2, sizeof(**dir));
489 (*dir)[ents] = xmalloc(sizeof(***dir)); 517 (*dir)[ents] = xmalloc(sizeof(***dir));
@@ -492,7 +520,7 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
492 memcpy(&(*dir)[ents]->a, a, sizeof(*a)); 520 memcpy(&(*dir)[ents]->a, a, sizeof(*a));
493 (*dir)[++ents] = NULL; 521 (*dir)[++ents] = NULL;
494 } 522 }
495 523 next:
496 xfree(filename); 524 xfree(filename);
497 xfree(longname); 525 xfree(longname);
498 } 526 }
@@ -547,7 +575,7 @@ do_rm(struct sftp_conn *conn, char *path)
547} 575}
548 576
549int 577int
550do_mkdir(struct sftp_conn *conn, char *path, Attrib *a) 578do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int printflag)
551{ 579{
552 u_int status, id; 580 u_int status, id;
553 581
@@ -556,7 +584,7 @@ do_mkdir(struct sftp_conn *conn, char *path, Attrib *a)
556 strlen(path), a); 584 strlen(path), a);
557 585
558 status = get_status(conn->fd_in, id); 586 status = get_status(conn->fd_in, id);
559 if (status != SSH2_FX_OK) 587 if (status != SSH2_FX_OK && printflag)
560 error("Couldn't create directory: %s", fx2txt(status)); 588 error("Couldn't create directory: %s", fx2txt(status));
561 589
562 return(status); 590 return(status);
@@ -895,9 +923,9 @@ send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
895 923
896int 924int
897do_download(struct sftp_conn *conn, char *remote_path, char *local_path, 925do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
898 int pflag) 926 Attrib *a, int pflag)
899{ 927{
900 Attrib junk, *a; 928 Attrib junk;
901 Buffer msg; 929 Buffer msg;
902 char *handle; 930 char *handle;
903 int local_fd, status = 0, write_error; 931 int local_fd, status = 0, write_error;
@@ -916,9 +944,8 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
916 944
917 TAILQ_INIT(&requests); 945 TAILQ_INIT(&requests);
918 946
919 a = do_stat(conn, remote_path, 0); 947 if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL)
920 if (a == NULL) 948 return -1;
921 return(-1);
922 949
923 /* Do not preserve set[ug]id here, as we do not preserve ownership */ 950 /* Do not preserve set[ug]id here, as we do not preserve ownership */
924 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) 951 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
@@ -951,7 +978,8 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
951 send_msg(conn->fd_out, &msg); 978 send_msg(conn->fd_out, &msg);
952 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path); 979 debug3("Sent message SSH2_FXP_OPEN I:%u P:%s", id, remote_path);
953 980
954 handle = get_handle(conn->fd_in, id, &handle_len); 981 handle = get_handle(conn->fd_in, id, &handle_len,
982 "remote open(\"%s\")", remote_path);
955 if (handle == NULL) { 983 if (handle == NULL) {
956 buffer_free(&msg); 984 buffer_free(&msg);
957 return(-1); 985 return(-1);
@@ -1132,6 +1160,114 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1132 return(status); 1160 return(status);
1133} 1161}
1134 1162
1163static int
1164download_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1165 Attrib *dirattrib, int pflag, int printflag, int depth)
1166{
1167 int i, ret = 0;
1168 SFTP_DIRENT **dir_entries;
1169 char *filename, *new_src, *new_dst;
1170 mode_t mode = 0777;
1171
1172 if (depth >= MAX_DIR_DEPTH) {
1173 error("Maximum directory depth exceeded: %d levels", depth);
1174 return -1;
1175 }
1176
1177 if (dirattrib == NULL &&
1178 (dirattrib = do_stat(conn, src, 1)) == NULL) {
1179 error("Unable to stat remote directory \"%s\"", src);
1180 return -1;
1181 }
1182 if (!S_ISDIR(dirattrib->perm)) {
1183 error("\"%s\" is not a directory", src);
1184 return -1;
1185 }
1186 if (printflag)
1187 printf("Retrieving %s\n", src);
1188
1189 if (dirattrib->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
1190 mode = dirattrib->perm & 01777;
1191 else {
1192 debug("Server did not send permissions for "
1193 "directory \"%s\"", dst);
1194 }
1195
1196 if (mkdir(dst, mode) == -1 && errno != EEXIST) {
1197 error("mkdir %s: %s", dst, strerror(errno));
1198 return -1;
1199 }
1200
1201 if (do_readdir(conn, src, &dir_entries) == -1) {
1202 error("%s: Failed to get directory contents", src);
1203 return -1;
1204 }
1205
1206 for (i = 0; dir_entries[i] != NULL && !interrupted; i++) {
1207 filename = dir_entries[i]->filename;
1208
1209 new_dst = path_append(dst, filename);
1210 new_src = path_append(src, filename);
1211
1212 if (S_ISDIR(dir_entries[i]->a.perm)) {
1213 if (strcmp(filename, ".") == 0 ||
1214 strcmp(filename, "..") == 0)
1215 continue;
1216 if (download_dir_internal(conn, new_src, new_dst,
1217 &(dir_entries[i]->a), pflag, printflag,
1218 depth + 1) == -1)
1219 ret = -1;
1220 } else if (S_ISREG(dir_entries[i]->a.perm) ) {
1221 if (do_download(conn, new_src, new_dst,
1222 &(dir_entries[i]->a), pflag) == -1) {
1223 error("Download of file %s to %s failed",
1224 new_src, new_dst);
1225 ret = -1;
1226 }
1227 } else
1228 logit("%s: not a regular file\n", new_src);
1229
1230 xfree(new_dst);
1231 xfree(new_src);
1232 }
1233
1234 if (pflag) {
1235 if (dirattrib->flags & SSH2_FILEXFER_ATTR_ACMODTIME) {
1236 struct timeval tv[2];
1237 tv[0].tv_sec = dirattrib->atime;
1238 tv[1].tv_sec = dirattrib->mtime;
1239 tv[0].tv_usec = tv[1].tv_usec = 0;
1240 if (utimes(dst, tv) == -1)
1241 error("Can't set times on \"%s\": %s",
1242 dst, strerror(errno));
1243 } else
1244 debug("Server did not send times for directory "
1245 "\"%s\"", dst);
1246 }
1247
1248 free_sftp_dirents(dir_entries);
1249
1250 return ret;
1251}
1252
1253int
1254download_dir(struct sftp_conn *conn, char *src, char *dst,
1255 Attrib *dirattrib, int pflag, int printflag)
1256{
1257 char *src_canon;
1258 int ret;
1259
1260 if ((src_canon = do_realpath(conn, src)) == NULL) {
1261 error("Unable to canonicalise path \"%s\"", src);
1262 return -1;
1263 }
1264
1265 ret = download_dir_internal(conn, src_canon, dst,
1266 dirattrib, pflag, printflag, 0);
1267 xfree(src_canon);
1268 return ret;
1269}
1270
1135int 1271int
1136do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, 1272do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1137 int pflag) 1273 int pflag)
@@ -1195,7 +1331,8 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1195 1331
1196 buffer_clear(&msg); 1332 buffer_clear(&msg);
1197 1333
1198 handle = get_handle(conn->fd_in, id, &handle_len); 1334 handle = get_handle(conn->fd_in, id, &handle_len,
1335 "remote open(\"%s\")", remote_path);
1199 if (handle == NULL) { 1336 if (handle == NULL) {
1200 close(local_fd); 1337 close(local_fd);
1201 buffer_free(&msg); 1338 buffer_free(&msg);
@@ -1313,3 +1450,127 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1313 1450
1314 return status; 1451 return status;
1315} 1452}
1453
1454static int
1455upload_dir_internal(struct sftp_conn *conn, char *src, char *dst,
1456 int pflag, int printflag, int depth)
1457{
1458 int ret = 0, status;
1459 DIR *dirp;
1460 struct dirent *dp;
1461 char *filename, *new_src, *new_dst;
1462 struct stat sb;
1463 Attrib a;
1464
1465 if (depth >= MAX_DIR_DEPTH) {
1466 error("Maximum directory depth exceeded: %d levels", depth);
1467 return -1;
1468 }
1469
1470 if (stat(src, &sb) == -1) {
1471 error("Couldn't stat directory \"%s\": %s",
1472 src, strerror(errno));
1473 return -1;
1474 }
1475 if (!S_ISDIR(sb.st_mode)) {
1476 error("\"%s\" is not a directory", src);
1477 return -1;
1478 }
1479 if (printflag)
1480 printf("Entering %s\n", src);
1481
1482 attrib_clear(&a);
1483 stat_to_attrib(&sb, &a);
1484 a.flags &= ~SSH2_FILEXFER_ATTR_SIZE;
1485 a.flags &= ~SSH2_FILEXFER_ATTR_UIDGID;
1486 a.perm &= 01777;
1487 if (!pflag)
1488 a.flags &= ~SSH2_FILEXFER_ATTR_ACMODTIME;
1489
1490 status = do_mkdir(conn, dst, &a, 0);
1491 /*
1492 * we lack a portable status for errno EEXIST,
1493 * so if we get a SSH2_FX_FAILURE back we must check
1494 * if it was created successfully.
1495 */
1496 if (status != SSH2_FX_OK) {
1497 if (status != SSH2_FX_FAILURE)
1498 return -1;
1499 if (do_stat(conn, dst, 0) == NULL)
1500 return -1;
1501 }
1502
1503 if ((dirp = opendir(src)) == NULL) {
1504 error("Failed to open dir \"%s\": %s", src, strerror(errno));
1505 return -1;
1506 }
1507
1508 while (((dp = readdir(dirp)) != NULL) && !interrupted) {
1509 if (dp->d_ino == 0)
1510 continue;
1511 filename = dp->d_name;
1512 new_dst = path_append(dst, filename);
1513 new_src = path_append(src, filename);
1514
1515 if (lstat(new_src, &sb) == -1) {
1516 logit("%s: lstat failed: %s", filename,
1517 strerror(errno));
1518 ret = -1;
1519 } else if (S_ISDIR(sb.st_mode)) {
1520 if (strcmp(filename, ".") == 0 ||
1521 strcmp(filename, "..") == 0)
1522 continue;
1523
1524 if (upload_dir_internal(conn, new_src, new_dst,
1525 pflag, depth + 1, printflag) == -1)
1526 ret = -1;
1527 } else if (S_ISREG(sb.st_mode)) {
1528 if (do_upload(conn, new_src, new_dst, pflag) == -1) {
1529 error("Uploading of file %s to %s failed!",
1530 new_src, new_dst);
1531 ret = -1;
1532 }
1533 } else
1534 logit("%s: not a regular file\n", filename);
1535 xfree(new_dst);
1536 xfree(new_src);
1537 }
1538
1539 do_setstat(conn, dst, &a);
1540
1541 (void) closedir(dirp);
1542 return ret;
1543}
1544
1545int
1546upload_dir(struct sftp_conn *conn, char *src, char *dst, int printflag,
1547 int pflag)
1548{
1549 char *dst_canon;
1550 int ret;
1551
1552 if ((dst_canon = do_realpath(conn, dst)) == NULL) {
1553 error("Unable to canonicalise path \"%s\"", dst);
1554 return -1;
1555 }
1556
1557 ret = upload_dir_internal(conn, src, dst_canon, pflag, printflag, 0);
1558 xfree(dst_canon);
1559 return ret;
1560}
1561
1562char *
1563path_append(char *p1, char *p2)
1564{
1565 char *ret;
1566 size_t len = strlen(p1) + strlen(p2) + 2;
1567
1568 ret = xmalloc(len);
1569 strlcpy(ret, p1, len);
1570 if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
1571 strlcat(ret, "/", len);
1572 strlcat(ret, p2, len);
1573
1574 return(ret);
1575}
1576