summaryrefslogtreecommitdiff
path: root/sftp-client.c
diff options
context:
space:
mode:
authorDarren Tucker <dtucker@zip.com.au>2009-10-07 08:37:48 +1100
committerDarren Tucker <dtucker@zip.com.au>2009-10-07 08:37:48 +1100
commit1b0dd175377c86abb7f3a3da2e9b1a89f36c7a1a (patch)
treed6b5f501c4c9890ed91ce4fd86be583213d7e8e6 /sftp-client.c
parent1477ea162c05c09b4b5ebc19ac588fbf469349dc (diff)
- djm@cvs.openbsd.org 2009/08/18 18:36:21
[sftp-client.h sftp.1 sftp-client.c sftp.c] recursive transfer support for get/put and on the commandline work mostly by carlosvsilvapt@gmail.com for the Google Summer of Code with some tweaks by me; "go for it" deraadt@
Diffstat (limited to 'sftp-client.c')
-rw-r--r--sftp-client.c260
1 files changed, 251 insertions, 9 deletions
diff --git a/sftp-client.c b/sftp-client.c
index 14c172d2f..cc4a5b15b 100644
--- a/sftp-client.c
+++ b/sftp-client.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: sftp-client.c,v 1.88 2009/08/14 18:17:49 djm Exp $ */ 1/* $OpenBSD: sftp-client.c,v 1.89 2009/08/18 18:36:20 djm 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;
@@ -497,6 +501,17 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
497 if (printflag) 501 if (printflag)
498 printf("%s\n", longname); 502 printf("%s\n", longname);
499 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
500 if (dir) { 515 if (dir) {
501 *dir = xrealloc(*dir, ents + 2, sizeof(**dir)); 516 *dir = xrealloc(*dir, ents + 2, sizeof(**dir));
502 (*dir)[ents] = xmalloc(sizeof(***dir)); 517 (*dir)[ents] = xmalloc(sizeof(***dir));
@@ -505,7 +520,7 @@ do_lsreaddir(struct sftp_conn *conn, char *path, int printflag,
505 memcpy(&(*dir)[ents]->a, a, sizeof(*a)); 520 memcpy(&(*dir)[ents]->a, a, sizeof(*a));
506 (*dir)[++ents] = NULL; 521 (*dir)[++ents] = NULL;
507 } 522 }
508 523 next:
509 xfree(filename); 524 xfree(filename);
510 xfree(longname); 525 xfree(longname);
511 } 526 }
@@ -560,7 +575,7 @@ do_rm(struct sftp_conn *conn, char *path)
560} 575}
561 576
562int 577int
563do_mkdir(struct sftp_conn *conn, char *path, Attrib *a) 578do_mkdir(struct sftp_conn *conn, char *path, Attrib *a, int printflag)
564{ 579{
565 u_int status, id; 580 u_int status, id;
566 581
@@ -569,7 +584,7 @@ do_mkdir(struct sftp_conn *conn, char *path, Attrib *a)
569 strlen(path), a); 584 strlen(path), a);
570 585
571 status = get_status(conn->fd_in, id); 586 status = get_status(conn->fd_in, id);
572 if (status != SSH2_FX_OK) 587 if (status != SSH2_FX_OK && printflag)
573 error("Couldn't create directory: %s", fx2txt(status)); 588 error("Couldn't create directory: %s", fx2txt(status));
574 589
575 return(status); 590 return(status);
@@ -908,9 +923,9 @@ send_read_request(int fd_out, u_int id, u_int64_t offset, u_int len,
908 923
909int 924int
910do_download(struct sftp_conn *conn, char *remote_path, char *local_path, 925do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
911 int pflag) 926 Attrib *a, int pflag)
912{ 927{
913 Attrib junk, *a; 928 Attrib junk;
914 Buffer msg; 929 Buffer msg;
915 char *handle; 930 char *handle;
916 int local_fd, status = 0, write_error; 931 int local_fd, status = 0, write_error;
@@ -929,9 +944,8 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
929 944
930 TAILQ_INIT(&requests); 945 TAILQ_INIT(&requests);
931 946
932 a = do_stat(conn, remote_path, 0); 947 if (a == NULL && (a = do_stat(conn, remote_path, 0)) == NULL)
933 if (a == NULL) 948 return -1;
934 return(-1);
935 949
936 /* 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 */
937 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) 951 if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS)
@@ -1146,6 +1160,114 @@ do_download(struct sftp_conn *conn, char *remote_path, char *local_path,
1146 return(status); 1160 return(status);
1147} 1161}
1148 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
1149int 1271int
1150do_upload(struct sftp_conn *conn, char *local_path, char *remote_path, 1272do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1151 int pflag) 1273 int pflag)
@@ -1328,3 +1450,123 @@ do_upload(struct sftp_conn *conn, char *local_path, char *remote_path,
1328 1450
1329 return status; 1451 return status;
1330} 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 (S_ISDIR(DTTOIF(dp->d_type))) {
1516 if (strcmp(filename, ".") == 0 ||
1517 strcmp(filename, "..") == 0)
1518 continue;
1519
1520 if (upload_dir_internal(conn, new_src, new_dst,
1521 pflag, depth + 1, printflag) == -1)
1522 ret = -1;
1523 } else if (S_ISREG(DTTOIF(dp->d_type)) ) {
1524 if (do_upload(conn, new_src, new_dst, pflag) == -1) {
1525 error("Uploading of file %s to %s failed!",
1526 new_src, new_dst);
1527 ret = -1;
1528 }
1529 } else
1530 logit("%s: not a regular file\n", filename);
1531 xfree(new_dst);
1532 xfree(new_src);
1533 }
1534
1535 do_setstat(conn, dst, &a);
1536
1537 (void) closedir(dirp);
1538 return ret;
1539}
1540
1541int
1542upload_dir(struct sftp_conn *conn, char *src, char *dst, int printflag,
1543 int pflag)
1544{
1545 char *dst_canon;
1546 int ret;
1547
1548 if ((dst_canon = do_realpath(conn, dst)) == NULL) {
1549 error("Unable to canonicalise path \"%s\"", dst);
1550 return -1;
1551 }
1552
1553 ret = upload_dir_internal(conn, src, dst_canon, pflag, printflag, 0);
1554 xfree(dst_canon);
1555 return ret;
1556}
1557
1558char *
1559path_append(char *p1, char *p2)
1560{
1561 char *ret;
1562 size_t len = strlen(p1) + strlen(p2) + 2;
1563
1564 ret = xmalloc(len);
1565 strlcpy(ret, p1, len);
1566 if (p1[0] != '\0' && p1[strlen(p1) - 1] != '/')
1567 strlcat(ret, "/", len);
1568 strlcat(ret, p2, len);
1569
1570 return(ret);
1571}
1572