Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

SFTP/SCP: allow quote commands to fail if they're prefixed with '*'

example: "*rename file-to-fail.txt file-to.fail2.txt"
Signed-off-by: Jonas Schnelli <jonas.schnelli@include7.ch>
  • Loading branch information...
commit dd18d31b81c7bccff817d57bed1e709e2af7ded7 1 parent c8ffb40
Jonas Schnelli authored
80 lib/ssh.c
@@ -1063,7 +1063,20 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
1063 1063 /*
1064 1064 * Support some of the "FTP" commands
1065 1065 */
1066   - if(curl_strequal("pwd", sshc->quote_item->data)) {
  1066 + char *cmd = sshc->quote_item->data;
  1067 + sshc->acceptfail = FALSE;
  1068 +
  1069 + /* if a command starts with an asterisk, which a legal SFTP command never
  1070 + can, the command will be allowed to fail without it causing any
  1071 + aborts or cancels etc. It will cause libcurl to act as if the command
  1072 + is successful, whatever the server reponds. */
  1073 +
  1074 + if(cmd[0] == '*') {
  1075 + cmd++;
  1076 + sshc->acceptfail = TRUE;
  1077 + }
  1078 +
  1079 + if(curl_strequal("pwd", cmd)) {
1067 1080 /* output debug output if that is requested */
1068 1081 char *tmp = aprintf("257 \"%s\" is current directory.\n",
1069 1082 sftp_scp->path);
@@ -1085,12 +1098,12 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
1085 1098 state(conn, SSH_SFTP_NEXT_QUOTE);
1086 1099 break;
1087 1100 }
1088   - else if(sshc->quote_item->data) {
  1101 + else if(cmd) {
1089 1102 /*
1090 1103 * the arguments following the command must be separated from the
1091 1104 * command with a space so we can check for it unconditionally
1092 1105 */
1093   - cp = strchr(sshc->quote_item->data, ' ');
  1106 + cp = strchr(cmd, ' ');
1094 1107 if(cp == NULL) {
1095 1108 failf(data, "Syntax error in SFTP command. Supply parameter(s)!");
1096 1109 state(conn, SSH_SFTP_CLOSE);
@@ -1121,9 +1134,9 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
1121 1134 * OpenSSH's sftp program and call the appropriate libssh2
1122 1135 * functions.
1123 1136 */
1124   - if(curl_strnequal(sshc->quote_item->data, "chgrp ", 6) ||
1125   - curl_strnequal(sshc->quote_item->data, "chmod ", 6) ||
1126   - curl_strnequal(sshc->quote_item->data, "chown ", 6) ) {
  1137 + if(curl_strnequal(cmd, "chgrp ", 6) ||
  1138 + curl_strnequal(cmd, "chmod ", 6) ||
  1139 + curl_strnequal(cmd, "chown ", 6) ) {
1127 1140 /* attribute change */
1128 1141
1129 1142 /* sshc->quote_path1 contains the mode to set */
@@ -1146,8 +1159,8 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
1146 1159 state(conn, SSH_SFTP_QUOTE_STAT);
1147 1160 break;
1148 1161 }
1149   - else if(curl_strnequal(sshc->quote_item->data, "ln ", 3) ||
1150   - curl_strnequal(sshc->quote_item->data, "symlink ", 8)) {
  1162 + else if(curl_strnequal(cmd, "ln ", 3) ||
  1163 + curl_strnequal(cmd, "symlink ", 8)) {
1151 1164 /* symbolic linking */
1152 1165 /* sshc->quote_path1 is the source */
1153 1166 /* get the destination */
@@ -1168,12 +1181,12 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
1168 1181 state(conn, SSH_SFTP_QUOTE_SYMLINK);
1169 1182 break;
1170 1183 }
1171   - else if(curl_strnequal(sshc->quote_item->data, "mkdir ", 6)) {
  1184 + else if(curl_strnequal(cmd, "mkdir ", 6)) {
1172 1185 /* create dir */
1173 1186 state(conn, SSH_SFTP_QUOTE_MKDIR);
1174 1187 break;
1175 1188 }
1176   - else if(curl_strnequal(sshc->quote_item->data, "rename ", 7)) {
  1189 + else if(curl_strnequal(cmd, "rename ", 7)) {
1177 1190 /* rename file */
1178 1191 /* first param is the source path */
1179 1192 /* second param is the dest. path */
@@ -1193,12 +1206,12 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
1193 1206 state(conn, SSH_SFTP_QUOTE_RENAME);
1194 1207 break;
1195 1208 }
1196   - else if(curl_strnequal(sshc->quote_item->data, "rmdir ", 6)) {
  1209 + else if(curl_strnequal(cmd, "rmdir ", 6)) {
1197 1210 /* delete dir */
1198 1211 state(conn, SSH_SFTP_QUOTE_RMDIR);
1199 1212 break;
1200 1213 }
1201   - else if(curl_strnequal(sshc->quote_item->data, "rm ", 3)) {
  1214 + else if(curl_strnequal(cmd, "rm ", 3)) {
1202 1215 state(conn, SSH_SFTP_QUOTE_UNLINK);
1203 1216 break;
1204 1217 }
@@ -1246,7 +1259,21 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
1246 1259 break;
1247 1260
1248 1261 case SSH_SFTP_QUOTE_STAT:
1249   - if(!curl_strnequal(sshc->quote_item->data, "chmod", 5)) {
  1262 + {
  1263 + char *cmd = sshc->quote_item->data;
  1264 + sshc->acceptfail = FALSE;
  1265 +
  1266 + /* if a command starts with an asterisk, which a legal SFTP command never
  1267 + can, the command will be allowed to fail without it causing any
  1268 + aborts or cancels etc. It will cause libcurl to act as if the command
  1269 + is successful, whatever the server reponds. */
  1270 +
  1271 + if(cmd[0] == '*') {
  1272 + cmd++;
  1273 + sshc->acceptfail = TRUE;
  1274 + }
  1275 +
  1276 + if(!curl_strnequal(cmd, "chmod", 5)) {
1250 1277 /* Since chown and chgrp only set owner OR group but libssh2 wants to
1251 1278 * set them both at once, we need to obtain the current ownership
1252 1279 * first. This takes an extra protocol round trip.
@@ -1258,7 +1285,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
1258 1285 if(rc == LIBSSH2_ERROR_EAGAIN) {
1259 1286 break;
1260 1287 }
1261   - else if(rc != 0) { /* get those attributes */
  1288 + else if(rc != 0 && !sshc->acceptfail) { /* get those attributes */
1262 1289 err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
1263 1290 Curl_safefree(sshc->quote_path1);
1264 1291 sshc->quote_path1 = NULL;
@@ -1274,10 +1301,11 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
1274 1301 }
1275 1302
1276 1303 /* Now set the new attributes... */
1277   - if(curl_strnequal(sshc->quote_item->data, "chgrp", 5)) {
  1304 + if(curl_strnequal(cmd, "chgrp", 5)) {
1278 1305 sshc->quote_attrs.gid = strtoul(sshc->quote_path1, NULL, 10);
1279 1306 sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
1280   - if(sshc->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0])) {
  1307 + if(sshc->quote_attrs.gid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
  1308 + !sshc->acceptfail) {
1281 1309 Curl_safefree(sshc->quote_path1);
1282 1310 sshc->quote_path1 = NULL;
1283 1311 Curl_safefree(sshc->quote_path2);
@@ -1289,7 +1317,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
1289 1317 break;
1290 1318 }
1291 1319 }
1292   - else if(curl_strnequal(sshc->quote_item->data, "chmod", 5)) {
  1320 + else if(curl_strnequal(cmd, "chmod", 5)) {
1293 1321 sshc->quote_attrs.permissions = strtoul(sshc->quote_path1, NULL, 8);
1294 1322 sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_PERMISSIONS;
1295 1323 /* permissions are octal */
@@ -1306,10 +1334,11 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
1306 1334 break;
1307 1335 }
1308 1336 }
1309   - else if(curl_strnequal(sshc->quote_item->data, "chown", 5)) {
  1337 + else if(curl_strnequal(cmd, "chown", 5)) {
1310 1338 sshc->quote_attrs.uid = strtoul(sshc->quote_path1, NULL, 10);
1311 1339 sshc->quote_attrs.flags = LIBSSH2_SFTP_ATTR_UIDGID;
1312   - if(sshc->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0])) {
  1340 + if(sshc->quote_attrs.uid == 0 && !ISDIGIT(sshc->quote_path1[0]) &&
  1341 + !sshc->acceptfail) {
1313 1342 Curl_safefree(sshc->quote_path1);
1314 1343 sshc->quote_path1 = NULL;
1315 1344 Curl_safefree(sshc->quote_path2);
@@ -1325,6 +1354,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
1325 1354 /* Now send the completed structure... */
1326 1355 state(conn, SSH_SFTP_QUOTE_SETSTAT);
1327 1356 break;
  1357 + }
1328 1358
1329 1359 case SSH_SFTP_QUOTE_SETSTAT:
1330 1360 rc = libssh2_sftp_stat_ex(sshc->sftp_session, sshc->quote_path2,
@@ -1334,7 +1364,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
1334 1364 if(rc == LIBSSH2_ERROR_EAGAIN) {
1335 1365 break;
1336 1366 }
1337   - else if(rc != 0) {
  1367 + else if(rc != 0 && !sshc->acceptfail) {
1338 1368 err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
1339 1369 Curl_safefree(sshc->quote_path1);
1340 1370 sshc->quote_path1 = NULL;
@@ -1359,7 +1389,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
1359 1389 if(rc == LIBSSH2_ERROR_EAGAIN) {
1360 1390 break;
1361 1391 }
1362   - else if(rc != 0) {
  1392 + else if(rc != 0 && !sshc->acceptfail) {
1363 1393 err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
1364 1394 Curl_safefree(sshc->quote_path1);
1365 1395 sshc->quote_path1 = NULL;
@@ -1382,7 +1412,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
1382 1412 if(rc == LIBSSH2_ERROR_EAGAIN) {
1383 1413 break;
1384 1414 }
1385   - else if(rc != 0) {
  1415 + else if(rc != 0 && !sshc->acceptfail) {
1386 1416 err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
1387 1417 Curl_safefree(sshc->quote_path1);
1388 1418 sshc->quote_path1 = NULL;
@@ -1407,7 +1437,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
1407 1437 if(rc == LIBSSH2_ERROR_EAGAIN) {
1408 1438 break;
1409 1439 }
1410   - else if(rc != 0) {
  1440 + else if(rc != 0 && !sshc->acceptfail) {
1411 1441 err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
1412 1442 Curl_safefree(sshc->quote_path1);
1413 1443 sshc->quote_path1 = NULL;
@@ -1428,7 +1458,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
1428 1458 if(rc == LIBSSH2_ERROR_EAGAIN) {
1429 1459 break;
1430 1460 }
1431   - else if(rc != 0) {
  1461 + else if(rc != 0 && !sshc->acceptfail) {
1432 1462 err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
1433 1463 Curl_safefree(sshc->quote_path1);
1434 1464 sshc->quote_path1 = NULL;
@@ -1447,7 +1477,7 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
1447 1477 if(rc == LIBSSH2_ERROR_EAGAIN) {
1448 1478 break;
1449 1479 }
1450   - else if(rc != 0) {
  1480 + else if(rc != 0 && !sshc->acceptfail) {
1451 1481 err = (int)(libssh2_sftp_last_error(sshc->sftp_session));
1452 1482 Curl_safefree(sshc->quote_path1);
1453 1483 sshc->quote_path1 = NULL;
1  lib/ssh.h
@@ -115,6 +115,7 @@ struct ssh_conn {
115 115 char *quote_path1; /* two generic pointers for the QUOTE stuff */
116 116 char *quote_path2;
117 117 LIBSSH2_SFTP_ATTRIBUTES quote_attrs; /* used by the SFTP_QUOTE state */
  118 + bool acceptfail; /* accept file in SFTP_QUOTE state */
118 119 char *homedir; /* when doing SFTP we figure out home dir in the
119 120 connect phase */
120 121
5 tests/data/Makefile.am
@@ -53,8 +53,9 @@ test600 test601 test602 test603 test604 \
53 53 test605 test606 test607 test608 test609 test610 test611 test612 test613 \
54 54 test614 test615 test616 test617 test618 test619 test620 test621 test622 \
55 55 test623 test624 test625 test626 test627 test628 test629 test630 test631 \
56   -test632 test633 test634 test635 test636 test637 test700 test701 test702 \
57   -test703 test704 test705 test706 test707 test708 test709 test710 \
  56 +test632 test633 test634 test635 test636 test637 test638 test639 \
  57 +test700 test701 test702 test703 test704 test705 test706 test707 test708 \
  58 +test709 test710 \
58 59 test800 test801 test802 \
59 60 test803 test804 test805 test806 test807 test808 test809 test810 test811 \
60 61 test812 test813 test814 \
49 tests/data/test638
... ... @@ -0,0 +1,49 @@
  1 +<testcase>
  2 +<info>
  3 +<keywords>
  4 +SFTP
  5 +post-quote
  6 +acceptfail
  7 +asterisk
  8 +</keywords>
  9 +</info>
  10 +
  11 +#
  12 +# Server-side
  13 +<reply>
  14 +<data>
  15 +Dummy test file for rename test
  16 +</data>
  17 +</reply>
  18 +
  19 +#
  20 +# Client-side
  21 +<client>
  22 +<server>
  23 +sftp
  24 +</server>
  25 +<precheck>
  26 +perl %SRCDIR/libtest/test610.pl mkdir %PWD/log/test638.dir
  27 +</precheck>
  28 + <name>
  29 +SFTP post-quote rename * asterisk accept-fail
  30 + </name>
  31 + <command>
  32 +--key curl_client_key --pubkey curl_client_key.pub -u %USER: -Q "-*rename %PWD/log/test638.dir %PWD/log/test638.new" sftp://%HOSTIP:%SSHPORT%PWD/log/file638.txt --insecure
  33 +</command>
  34 +<postcheck>
  35 +perl %SRCDIR/libtest/test610.pl rmdir %PWD/log/test638.new
  36 +</postcheck>
  37 +<file name="log/file638.txt">
  38 +Dummy test file for rename test
  39 +</file>
  40 +</client>
  41 +
  42 +#
  43 +# Verify data after the test has been "shot"
  44 +<verify>
  45 +<valgrind>
  46 +disable
  47 +</valgrind>
  48 +</verify>
  49 +</testcase>
49 tests/data/test639
... ... @@ -0,0 +1,49 @@
  1 +<testcase>
  2 +<info>
  3 +<keywords>
  4 +SFTP
  5 +post-quote
  6 +acceptfail
  7 +asterisk
  8 +</keywords>
  9 +</info>
  10 +
  11 +#
  12 +# Server-side
  13 +<reply>
  14 +<data>
  15 +Dummy test file for rename test
  16 +</data>
  17 +</reply>
  18 +
  19 +#
  20 +# Client-side
  21 +<client>
  22 +<server>
  23 +sftp
  24 +</server>
  25 +<precheck>
  26 +perl %SRCDIR/libtest/test610.pl mkdir %PWD/log/test639.dir
  27 +</precheck>
  28 + <name>
  29 +SFTP post-quote rename * asterisk accept-fail
  30 + </name>
  31 + <command>
  32 +--key curl_client_key --pubkey curl_client_key.pub -u %USER: -Q "-*rename %PWD/log/test639-not-exists-dir %PWD/log/test639.new" sftp://%HOSTIP:%SSHPORT%PWD/log/file639.txt --insecure
  33 +</command>
  34 +<postcheck>
  35 +perl %SRCDIR/libtest/test610.pl rmdir %PWD/log/test639.dir
  36 +</postcheck>
  37 +<file name="log/file639.txt">
  38 +Dummy test file for rename test
  39 +</file>
  40 +</client>
  41 +
  42 +#
  43 +# Verify data after the test has been "shot"
  44 +<verify>
  45 +<valgrind>
  46 +disable
  47 +</valgrind>
  48 +</verify>
  49 +</testcase>

0 comments on commit dd18d31

Please sign in to comment.
Something went wrong with that request. Please try again.