Skip to content

Commit ca803ec

Browse files
yosuke-wolfsslejohnstown
authored andcommitted
Add a safe mask for peer-supplied mode and extend wolfSSH_SFTP_Open
1 parent 4ebb6be commit ca803ec

4 files changed

Lines changed: 275 additions & 57 deletions

File tree

examples/sftpclient/sftpclient.c

Lines changed: 164 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ static void ShowCommands(void)
357357
printf("\n\nCommands :\n");
358358
printf("\tcd <string> change directory\n");
359359
printf("\tchmod <mode> <path> change mode\n");
360+
printf("\tcreat <mode> <path> create file with given permissions\n");
360361
printf("\tget <remote file> <local file> pulls file(s) from server\n");
361362
printf("\tlcd <path> change local directory\n");
362363
printf("\tlls list local directory\n");
@@ -433,6 +434,76 @@ static INLINE char* SFTP_FGETS(func_args* args, char* msg, int msgSz)
433434
}
434435

435436

437+
/*
438+
* Parse "<mode> <path>" from a SFTP command argument string.
439+
* pt: points past the command keyword (caller advanced by sizeof).
440+
* modeBuf: caller buffer of WOLFSSH_MAX_OCTET_LEN bytes; receives the
441+
* null-terminated octal mode token on success.
442+
* pathOut: receives a pointer to the resolved remote path on success.
443+
* allocOut: receives a malloc'd absolute-path buffer when pt was relative
444+
* (caller must WFREE); NULL when the path was already absolute.
445+
* remoteDir: current remote working directory.
446+
* Returns 0 on success,
447+
* 1 if no mode token found (caller prints error and continues),
448+
* -1 on malloc failure (caller returns -1).
449+
*/
450+
static int sftpParseModeAndPath(char* pt, char* modeBuf, char** pathOut,
451+
char** allocOut, const char* remoteDir)
452+
{
453+
word32 sz, idx;
454+
char* f;
455+
456+
*allocOut = NULL;
457+
*pathOut = NULL;
458+
459+
sz = (word32)WSTRLEN(pt);
460+
if (sz > 0 && pt[sz - 1] == '\n') {
461+
pt[sz - 1] = '\0';
462+
sz--;
463+
}
464+
465+
for (idx = 0; idx < sz && pt[0] == ' '; idx++, pt++);
466+
sz = (word32)WSTRLEN(pt);
467+
468+
sz = (sz < WOLFSSH_MAX_OCTET_LEN - 1) ? sz : WOLFSSH_MAX_OCTET_LEN - 1;
469+
WMEMCPY(modeBuf, pt, sz);
470+
modeBuf[sz] = '\0';
471+
for (idx = 0; idx < sz; idx++) {
472+
if (modeBuf[idx] == ' ') {
473+
modeBuf[idx] = '\0';
474+
break;
475+
}
476+
}
477+
if (idx == 0 || (idx == sz && pt[sz] != ' '))
478+
return 1;
479+
480+
pt += (word32)WSTRLEN(modeBuf);
481+
sz = (word32)WSTRLEN(pt);
482+
for (idx = 0; idx < sz && pt[0] == ' '; idx++, pt++);
483+
484+
if (pt[0] == '\0')
485+
return 1;
486+
487+
if (pt[0] != '/') {
488+
int maxSz = (int)WSTRLEN(remoteDir) + (int)WSTRLEN(pt) + 2;
489+
f = (char*)WMALLOC(maxSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
490+
if (f == NULL)
491+
return -1;
492+
f[0] = '\0';
493+
WSTRNCAT(f, remoteDir, maxSz);
494+
if (WSTRLEN(remoteDir) > 1)
495+
WSTRNCAT(f, "/", maxSz);
496+
WSTRNCAT(f, pt, maxSz);
497+
*allocOut = f;
498+
*pathOut = f;
499+
}
500+
else {
501+
*pathOut = pt;
502+
}
503+
return 0;
504+
}
505+
506+
436507
/* main loop for handling commands */
437508
static int doCmds(func_args* args)
438509
{
@@ -822,54 +893,20 @@ static int doCmds(func_args* args)
822893
}
823894

824895
if ((pt = WSTRNSTR(msg, "chmod", MAX_CMD_SZ)) != NULL) {
825-
word32 sz, idx;
826896
char* f = NULL;
827-
char mode[WOLFSSH_MAX_OCTET_LEN];
897+
char* path;
898+
char mode[WOLFSSH_MAX_OCTET_LEN];
899+
int parseRet;
828900

829901
pt += sizeof("chmod");
830-
sz = (word32)WSTRLEN(pt);
831-
832-
if (sz > 0 && pt[sz - 1] == '\n') pt[sz - 1] = '\0';
833-
834-
/* advance pointer to first location of non space character */
835-
for (idx = 0; idx < sz && pt[0] == ' '; idx++, pt++);
836-
sz = (word32)WSTRLEN(pt);
837-
838-
/* get mode */
839-
sz = (sz < WOLFSSH_MAX_OCTET_LEN - 1)? sz :
840-
WOLFSSH_MAX_OCTET_LEN -1;
841-
WMEMCPY(mode, pt, sz);
842-
mode[WOLFSSH_MAX_OCTET_LEN - 1] = '\0';
843-
for (idx = 0; idx < sz; idx++) {
844-
if (mode[idx] == ' ') {
845-
mode[idx] = '\0';
846-
break;
847-
}
848-
}
849-
if (idx == 0) {
902+
parseRet = sftpParseModeAndPath(pt, mode, &path, &f, workingDir);
903+
if (parseRet == 1) {
850904
printf("error with getting mode\r\n");
851905
continue;
852906
}
853-
pt += (word32)WSTRLEN(mode);
854-
sz = (word32)WSTRLEN(pt);
855-
for (idx = 0; idx < sz && pt[0] == ' '; idx++, pt++);
856-
857-
if (pt[0] != '/') {
858-
int maxSz = (int)WSTRLEN(workingDir) + sz + 2;
859-
f = (char*)WMALLOC(maxSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
860-
if (f == NULL) {
861-
err_msg("Error malloc'ing");
862-
return -1;
863-
}
864-
865-
f[0] = '\0';
866-
WSTRNCAT(f, workingDir, maxSz);
867-
if (WSTRLEN(workingDir) > 1) {
868-
WSTRNCAT(f, "/", maxSz);
869-
}
870-
WSTRNCAT(f, pt, maxSz);
871-
872-
pt = f;
907+
if (parseRet == -1) {
908+
err_msg("Error malloc'ing");
909+
return -1;
873910
}
874911

875912
/* update permissions */
@@ -881,21 +918,104 @@ static int doCmds(func_args* args)
881918
}
882919
}
883920

884-
ret = wolfSSH_SFTP_CHMOD(ssh, pt, mode);
921+
ret = wolfSSH_SFTP_CHMOD(ssh, path, mode);
885922
err = wolfSSH_get_error(ssh);
886923
} while ((err == WS_WANT_READ || err == WS_WANT_WRITE ||
887924
err == WS_REKEYING) && ret != WS_SUCCESS);
888925
if (ret != WS_SUCCESS) {
889926
if (SFTP_FPUTS(args, "Unable to change permissions of ") < 0) {
890927
err_msg("fputs error");
928+
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
891929
return -1;
892930
}
893-
if (SFTP_FPUTS(args, pt) < 0) {
931+
if (SFTP_FPUTS(args, path) < 0) {
894932
err_msg("fputs error");
933+
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
895934
return -1;
896935
}
897936
if (SFTP_FPUTS(args, "\n") < 0) {
898937
err_msg("fputs error");
938+
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
939+
return -1;
940+
}
941+
}
942+
943+
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
944+
continue;
945+
}
946+
947+
if ((pt = WSTRNSTR(msg, "creat", MAX_CMD_SZ)) != NULL) {
948+
char* f = NULL;
949+
char* path;
950+
char mode[WOLFSSH_MAX_OCTET_LEN];
951+
byte handle[WOLFSSH_MAX_HANDLE];
952+
word32 handleSz = WOLFSSH_MAX_HANDLE;
953+
WS_SFTP_FILEATRB atr;
954+
int parseRet;
955+
int openRet = WS_FATAL_ERROR;
956+
unsigned long perVal;
957+
char* modeEnd;
958+
959+
pt += sizeof("creat");
960+
parseRet = sftpParseModeAndPath(pt, mode, &path, &f, workingDir);
961+
if (parseRet == 1) {
962+
printf("error with getting mode\r\n");
963+
continue;
964+
}
965+
if (parseRet == -1) {
966+
err_msg("Error malloc'ing");
967+
return -1;
968+
}
969+
970+
/* build permission attribute from octal mode string;
971+
* wolfSSH_oct2dec is internal scope so strtoul is used here */
972+
perVal = strtoul(mode, &modeEnd, 8);
973+
if (*modeEnd == '\0' && perVal <= 07777) {
974+
WMEMSET(&atr, 0, sizeof(WS_SFTP_FILEATRB));
975+
atr.flags = WOLFSSH_FILEATRB_PERM;
976+
atr.per = (word32)perVal;
977+
978+
/* open (create) remote file with the given permissions */
979+
handleSz = WOLFSSH_MAX_HANDLE;
980+
do {
981+
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
982+
ret = wolfSSH_worker(ssh, NULL);
983+
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
984+
ret = wolfSSH_get_error(ssh);
985+
}
986+
}
987+
ret = wolfSSH_SFTP_Open(ssh, path,
988+
WOLFSSH_FXF_WRITE | WOLFSSH_FXF_CREAT |
989+
WOLFSSH_FXF_TRUNC, &atr, handle, &handleSz);
990+
err = wolfSSH_get_error(ssh);
991+
} while ((err == WS_WANT_READ || err == WS_WANT_WRITE ||
992+
err == WS_REKEYING) && ret != WS_SUCCESS);
993+
openRet = ret;
994+
}
995+
if (openRet == WS_SUCCESS) {
996+
do {
997+
while (ret == WS_REKEYING || ssh->error == WS_REKEYING) {
998+
ret = wolfSSH_worker(ssh, NULL);
999+
if (ret != WS_SUCCESS && ret == WS_FATAL_ERROR) {
1000+
ret = wolfSSH_get_error(ssh);
1001+
}
1002+
}
1003+
ret = wolfSSH_SFTP_Close(ssh, handle, handleSz);
1004+
err = wolfSSH_get_error(ssh);
1005+
} while ((err == WS_WANT_READ || err == WS_WANT_WRITE ||
1006+
err == WS_REKEYING) && ret != WS_SUCCESS);
1007+
if (ret != WS_SUCCESS) {
1008+
if (SFTP_FPUTS(args, "Unable to close file handle\n") < 0) {
1009+
err_msg("fputs error");
1010+
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
1011+
return -1;
1012+
}
1013+
}
1014+
}
1015+
else {
1016+
if (SFTP_FPUTS(args, "Unable to create file\n") < 0) {
1017+
err_msg("fputs error");
1018+
WFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER);
8991019
return -1;
9001020
}
9011021
}

src/wolfsftp.c

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1926,9 +1926,9 @@ int wolfSSH_SFTP_RecvMKDIR(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz)
19261926
#ifndef USE_WINDOWS_API
19271927
#ifndef WOLFSSH_FATFS
19281928
{
1929-
word32 mode = 040755;
1929+
word32 mode = 0755;
19301930
if (atr.flags & WOLFSSH_FILEATRB_PERM) {
1931-
mode = atr.per;
1931+
mode = WOLFSSH_SFTP_SAFE_MODE(atr.per);
19321932
}
19331933
else {
19341934
WLOG(WS_LOG_SFTP, "No permission attribute, using default");
@@ -2269,7 +2269,7 @@ int wolfSSH_SFTP_RecvOpen(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz)
22692269
}
22702270
}
22712271
#else
2272-
fd = WOPEN(ssh->fs, dir, m, atr.per);
2272+
fd = WOPEN(ssh->fs, dir, m, WOLFSSH_SFTP_SAFE_MODE(atr.per));
22732273
#endif
22742274
if (fd < 0) {
22752275
WLOG(WS_LOG_SFTP, "Error opening file %s", dir);
@@ -5717,7 +5717,7 @@ static int SFTP_SetFileAttributes(WOLFSSH* ssh,
57175717
#if !defined(USE_WINDOWS_API) && !defined(WOLFSSH_ZEPHYR)
57185718
/* check if permissions attribute present */
57195719
if (atr->flags & WOLFSSH_FILEATRB_PERM) {
5720-
ret = SFTP_SetMode(ssh->fs, name, atr->per);
5720+
ret = SFTP_SetMode(ssh->fs, name, WOLFSSH_SFTP_SAFE_MODE(atr->per));
57215721
}
57225722
#endif
57235723

@@ -5758,7 +5758,7 @@ static int SFTP_SetFileAttributesHandle(WOLFSSH* ssh,
57585758
#ifndef USE_WINDOWS_API
57595759
/* check if permissions attribute present */
57605760
if (atr->flags & WOLFSSH_FILEATRB_PERM) {
5761-
ret = SFTP_SetModeHandle(ssh->fs, handle, atr->per);
5761+
ret = SFTP_SetModeHandle(ssh->fs, handle, WOLFSSH_SFTP_SAFE_MODE(atr->per));
57625762
}
57635763
#endif
57645764

@@ -7423,7 +7423,8 @@ int wolfSSH_SFTP_SetSTAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr)
74237423
* dir NULL terminated string holding file name
74247424
* reason the reason file is being opened for i.e. WOLFSSH_FXF_READ,
74257425
* WOLFSSH_FXF_WRITE, ....
7426-
* atr attributes of file
7426+
* atr attributes serialized into the SSH_FXP_OPEN packet; NULL sends an
7427+
* empty attributes block (flags=0), preserving the prior behavior
74277428
* handle resulting handle from opening the file
74287429
* handleSz gets set to resulting handle size. Should initially be size of
74297430
* handle buffer when passed in
@@ -7436,6 +7437,7 @@ int wolfSSH_SFTP_Open(WOLFSSH* ssh, char* dir, word32 reason,
74367437
WS_SFTP_OPEN_STATE* state = NULL;
74377438
int ret = WS_SUCCESS;
74387439
int sz;
7440+
int atrSz;
74397441

74407442
WLOG(WS_LOG_SFTP, "Entering wolfSSH_SFTP_Open()");
74417443
if (ssh == NULL || dir == NULL) {
@@ -7463,8 +7465,10 @@ int wolfSSH_SFTP_Open(WOLFSSH* ssh, char* dir, word32 reason,
74637465
case STATE_OPEN_INIT:
74647466
WLOG(WS_LOG_SFTP, "SFTP OPEN STATE: INIT");
74657467
sz = (int)WSTRLEN(dir);
7468+
atrSz = (atr != NULL) ? SFTP_AttributesSz(ssh, atr) :
7469+
UINT32_SZ;
74667470
if (wolfSSH_SFTP_buffer_create(ssh, &state->buffer, sz +
7467-
WOLFSSH_SFTP_HEADER + UINT32_SZ * 3) !=
7471+
WOLFSSH_SFTP_HEADER + UINT32_SZ * 2 + atrSz) !=
74687472
WS_SUCCESS) {
74697473
ssh->error = WS_MEMORY_E;
74707474
ret = WS_FATAL_ERROR;
@@ -7473,7 +7477,7 @@ int wolfSSH_SFTP_Open(WOLFSSH* ssh, char* dir, word32 reason,
74737477
}
74747478

74757479
ret = SFTP_SetHeader(ssh, ssh->reqId, WOLFSSH_FTP_OPEN,
7476-
sz + UINT32_SZ * 3,
7480+
sz + UINT32_SZ * 2 + atrSz,
74777481
wolfSSH_SFTP_buffer_data(&state->buffer));
74787482
if (ret != WS_SUCCESS) {
74797483
state->state = STATE_OPEN_CLEANUP;
@@ -7490,9 +7494,20 @@ int wolfSSH_SFTP_Open(WOLFSSH* ssh, char* dir, word32 reason,
74907494
wolfSSH_SFTP_buffer_idx(&state->buffer), sz);
74917495
wolfSSH_SFTP_buffer_c32toa(&state->buffer, reason);
74927496

7493-
/* @TODO handle adding attributes here */
7494-
WOLFSSH_UNUSED(atr);
7495-
wolfSSH_SFTP_buffer_c32toa(&state->buffer, 0x00000000);
7497+
if (atr != NULL) {
7498+
ret = SFTP_SetAttributes(ssh,
7499+
wolfSSH_SFTP_buffer_data(&state->buffer) +
7500+
wolfSSH_SFTP_buffer_idx(&state->buffer), atrSz, atr);
7501+
if (ret != WS_SUCCESS) {
7502+
state->state = STATE_OPEN_CLEANUP;
7503+
continue;
7504+
}
7505+
wolfSSH_SFTP_buffer_seek(&state->buffer,
7506+
wolfSSH_SFTP_buffer_idx(&state->buffer), atrSz);
7507+
}
7508+
else {
7509+
wolfSSH_SFTP_buffer_c32toa(&state->buffer, 0x00000000);
7510+
}
74967511
ret = wolfSSH_SFTP_buffer_set_size(&state->buffer,
74977512
wolfSSH_SFTP_buffer_idx(&state->buffer));
74987513
if (ret != WS_SUCCESS) {

0 commit comments

Comments
 (0)