diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index 7518f816..9bae4498 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -35,6 +35,7 @@ #ifdef WOLFSSL_NUCLEUS /* use buffers for keys with server */ #define NO_FILESYSTEM + #define WOLFSSH_NO_EXIT #endif #ifdef NO_FILESYSTEM @@ -210,6 +211,12 @@ static THREAD_RETURN WOLFSSH_THREAD server_worker(void* vArgs) break; } + if (ret == WS_FATAL_ERROR && wolfSSH_get_error(threadCtx->ssh) == + WS_VERSION_E) { + ret = 0; /* don't break out of loop with version miss match */ + printf("Unsupported version error\n"); + } + if (wolfSSH_shutdown(threadCtx->ssh) != WS_SUCCESS) { fprintf(stderr, "Error with SSH shutdown.\n"); } @@ -220,7 +227,9 @@ static THREAD_RETURN WOLFSSH_THREAD server_worker(void* vArgs) if (ret != 0) { fprintf(stderr, "Error [%d] \"%s\" with handling connection.\n", ret, wolfSSH_ErrorToName(ret)); + #ifndef WOLFSSH_NO_EXIT exit(EXIT_FAILURE); + #endif } free(threadCtx); diff --git a/src/internal.c b/src/internal.c index 0a57a492..288461d2 100644 --- a/src/internal.c +++ b/src/internal.c @@ -7149,3 +7149,59 @@ void DumpOctetString(const byte* input, word32 inputSz) } #endif + +/* converts the octal input to decimal. Input is in string format i.e. 0666 + * returns the decimal value on success or negative value on failure */ +int wolfSSH_oct2dec(WOLFSSH* ssh, byte* oct, word32 octSz) +{ + int ret; + +#if defined(WOLFSSL_KEY_GEN) || defined(HAVE_COMP_KEY) || \ + defined(WOLFSSL_DEBUG_MATH) || defined(DEBUG_WOLFSSL) || \ + defined(WOLFSSL_PUBLIC_MP) + mp_int tmp; + char decimalString[WOLFSSH_MAX_OCTET_LEN + 1]; +#else + int i; +#endif + + if (octSz > WOLFSSH_MAX_OCTET_LEN || ssh == NULL || oct == NULL) { + return WS_BAD_ARGUMENT; + } + +#if defined(WOLFSSL_KEY_GEN) || defined(HAVE_COMP_KEY) || \ + defined(WOLFSSL_DEBUG_MATH) || defined(DEBUG_WOLFSSL) || \ + defined(WOLFSSL_PUBLIC_MP) + ret = mp_init(&tmp); + if (ret == MP_OKAY) { + ret = mp_read_radix(&tmp, (const char*)oct, 8); + } + + if (ret == MP_OKAY) { + /* convert octal to decimal */ + ret = mp_todecimal(&tmp, decimalString); + + if (ret == MP_OKAY) { + /* convert string to int */ + ret = atoi(decimalString); + } + } + mp_clear(&tmp); +#else + /* convert octal string to int without mp_read_radix() */ + ret = 0; + + for (i = 0; i < WOLFSSH_MAX_OCTET_LEN; i++) + { + if (oct[i] < '0' || oct[0] > '7') { + ret = WS_BAD_ARGUMENT; + break; + } + ret <<= 3; + ret |= (oct[i] - '0'); + } +#endif + + return ret; +} + diff --git a/src/ssh.c b/src/ssh.c index db0ad273..f3d76d62 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -930,5 +930,3 @@ const char* wolfSSH_GetSessionCommand(const WOLFSSH* ssh) return NULL; } - - diff --git a/src/wolfscp.c b/src/wolfscp.c index 723a5f8f..b204668b 100644 --- a/src/wolfscp.c +++ b/src/wolfscp.c @@ -827,6 +827,7 @@ static int GetScpFileMode(WOLFSSH* ssh, byte* buf, word32 bufSz, return ret; } + /* Locates first space present in given string (buf) and sets inOutIdx * to that offset. * diff --git a/src/wolfsftp.c b/src/wolfsftp.c index 8bd7ac72..6e2e7271 100644 --- a/src/wolfsftp.c +++ b/src/wolfsftp.c @@ -127,8 +127,7 @@ static int SFTP_ServerRecvInit(WOLFSSH* ssh) { ato32(buf + LENGTH_SZ + MSG_ID_SZ, &version); if (version != WOLFSSH_SFTP_VERSION) { - WLOG(WS_LOG_SFTP, "Unsupported SFTP version"); - return WS_FATAL_ERROR; + WLOG(WS_LOG_SFTP, "Unsupported SFTP version, sending version 3"); } /* silently ignore extensions if not supported */ @@ -263,7 +262,7 @@ static int SFTP_AtributesSz(WOLFSSH* ssh, WS_SFTP_FILEATRB* atr) * * returns WS_SUCCESS on success */ -static int SFTP_SetAtributes(WOLFSSH* ssh, byte* buf, word32 bufSz, +static int SFTP_SetAttributes(WOLFSSH* ssh, byte* buf, word32 bufSz, WS_SFTP_FILEATRB* atr) { word32 idx = 0; @@ -396,7 +395,7 @@ static int wolfSSH_SFTP_RecvRealPath(WOLFSSH* ssh, int reqId, int maxSz) WMEMCPY(out + idx, r, rSz); idx += rSz; /* set attributes */ - SFTP_SetAtributes(ssh, out + idx, maxSz - idx, &atr); + SFTP_SetAttributes(ssh, out + idx, maxSz - idx, &atr); /* send out buffer */ if (wolfSSH_stream_send(ssh, out, maxSz) < 0) { @@ -464,7 +463,10 @@ int wolfSSH_SFTP_read(WOLFSSH* ssh) case WOLFSSH_FTP_RENAME: return wolfSSH_SFTP_RecvRename(ssh, reqId, maxSz); - #ifndef NO_WOLFSSL_DIR + case WOLFSSH_FTP_SETSTAT: + return wolfSSH_SFTP_RecvSetSTAT(ssh, reqId, maxSz); + + #ifndef NO_WOLFSSH_DIR case WOLFSSH_FTP_OPENDIR: return wolfSSH_SFTP_RecvOpenDir(ssh, reqId, maxSz); @@ -673,7 +675,8 @@ int wolfSSH_SFTP_RecvMKDIR(WOLFSSH* ssh, int reqId, word32 maxSz) ato32(data + idx, &sz); idx += UINT32_SZ; if (sz != UINT32_SZ) { WLOG(WS_LOG_SFTP, "Attribute size larger than 4 not yet supported"); - WLOG(WS_LOG_SFTP, "Skipping over attribute"); + WLOG(WS_LOG_SFTP, "Skipping over attribute and using default"); + mode = 0x41ED; } else { ato32(data + idx, &mode); @@ -811,13 +814,14 @@ int wolfSSH_SFTP_RecvOpen(WOLFSSH* ssh, int reqId, word32 maxSz) } -#ifndef NO_WOLFSSL_DIR +#ifndef NO_WOLFSSH_DIR /* hold pointers to directory handles */ typedef struct DIR_HANDLE { WDIR dir; - byte isEof; /* flag for if read everything */ - word64 id; /* handle ID */ + char* dirName; /* base name of directory */ + byte isEof; /* flag for if read everything */ + word64 id; /* handle ID */ struct DIR_HANDLE* next; } DIR_HANDLE; static DIR_HANDLE* dirList = NULL; @@ -881,7 +885,6 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, word32 maxSz) "Unable To Open Directory", "English"); return WS_BAD_FILE_E; } - WFREE(dir, ssh->ctx->heap, DYNTYPE_BUFFER); (void)reqId; @@ -890,6 +893,7 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, word32 maxSz) dirList = (DIR_HANDLE*)WMALLOC(sizeof(DIR_HANDLE), ssh->ctx->heap, DYNTYPE_SFTP); if (dirList == NULL) { + WFREE(dir, ssh->ctx->heap, DYNTYPE_BUFFER); return WS_MEMORY_E; } #ifdef WOLFSSL_NUCLEUS @@ -900,6 +904,7 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, word32 maxSz) dirList->id = idCount++; dirList->isEof = 0; dirList->next = NULL; + dirList->dirName = dir; /* take over ownership of buffer */ SendPacketType(ssh, WOLFSSH_FTP_HANDLE, (byte*)&dirList->id, sizeof(word64)); } @@ -907,6 +912,7 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, word32 maxSz) DIR_HANDLE* cur = (DIR_HANDLE*)WMALLOC(sizeof(DIR_HANDLE), ssh->ctx->heap, DYNTYPE_SFTP); if (cur == NULL) { + WFREE(dir, ssh->ctx->heap, DYNTYPE_BUFFER); return WS_MEMORY_E; } #ifdef WOLFSSL_NUCLEUS @@ -918,6 +924,7 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, word32 maxSz) cur->isEof = 0; cur->next = dirList; dirList = cur; + dirList->dirName = dir; /* take over ownership of buffer */ SendPacketType(ssh, WOLFSSH_FTP_HANDLE, (byte*)&cur->id, sizeof(word64)); } @@ -933,7 +940,8 @@ int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, word32 maxSz) * * returns WS_SUCCESS on success */ -static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out) +static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out, + char* dirName) { int sz; @@ -941,12 +949,14 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out) return WS_BAD_ARGUMENT; } - sz = (int)WSTRLEN(dir->sfname); + /* use long name on Nucleus because sfname has only the file name and in all + * caps */ + sz = (int)WSTRLEN(dir->lfname); out->fName = (char*)WMALLOC(sz + 1, out->heap, DYNTYPE_SFTP); if (out->fName == NULL) { return WS_MEMORY_E; } - WMEMCPY(out->fName, dir->sfname, sz); + WMEMCPY(out->fName, dir->lfname, sz); out->fName[sz] = '\0'; out->fSz = sz; @@ -959,7 +969,33 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out) out->lName[sz] = '\0'; out->lSz = sz; - SFTP_GetAttributes(out->fName, &out->atrb, 0); + { + char* buf; + int bufSz; + int tmpSz; + + bufSz = out->fSz + WSTRLEN(dirName) + sizeof(WS_DELIM); + buf = (char*)XMALLOC(bufSz + 1, out->heap, DYNTYPE_SFTP); + if (buf == NULL) { + return WS_MEMORY_E; + } + buf[0] = '\0'; + WSTRNCAT(buf, dirName, bufSz); + tmpSz = WSTRLEN(buf); + + /* add delimiter between path and file/dir name */ + if (tmpSz + 1 < bufSz) { + buf[tmpSz] = WS_DELIM; + buf[tmpSz+1] = '\0'; + } + WSTRNCAT(buf, out->fName, bufSz); + + clean_path(buf); + if (SFTP_GetAttributes(buf, &out->atrb, 0) != WS_SUCCESS) { + WLOG(WS_LOG_SFTP, "Unable to get attribute values for %s", buf); + } + WFREE(buf, out->heap, DYNTYPE_SFTP); + } if ((WREADDIR(dir)) == NULL) { return WS_NEXT_ERROR; @@ -973,7 +1009,8 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out) * * returns WS_SUCCESS on success */ -static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out) +static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out, + char* dirName) { struct dirent* dp; int sz; @@ -1006,8 +1043,33 @@ static int wolfSSH_SFTPNAME_readdir(WOLFSSH* ssh, WDIR* dir, WS_SFTPNAME* out) out->lSz = sz; /* attempt to get file attributes. Could be directory or have none */ - if (SFTP_GetAttributes(out->fName, &out->atrb, 0) != WS_SUCCESS) { - WLOG(WS_LOG_SFTP, "Unable to get attribute values for %s", out->fName); + { + char* buf; + int bufSz; + int tmpSz; + + bufSz = out->fSz + WSTRLEN(dirName) + sizeof(WS_DELIM); + buf = (char*)XMALLOC(bufSz + 1, out->heap, DYNTYPE_SFTP); + if (buf == NULL) { + return WS_MEMORY_E; + } + buf[0] = '\0'; + WSTRNCAT(buf, dirName, bufSz); + tmpSz = WSTRLEN(buf); + + /* add delimiter between path and file/dir name */ + if (tmpSz + 1 < bufSz) { + buf[tmpSz] = WS_DELIM; + buf[tmpSz+1] = '\0'; + } + WSTRNCAT(buf, out->fName, bufSz); + + clean_path(buf); + if (SFTP_GetAttributes(buf, &out->atrb, 0) != WS_SUCCESS) { + WLOG(WS_LOG_SFTP, "Unable to get attribute values for %s", + out->fName); + } + WFREE(buf, out->heap, DYNTYPE_SFTP); } return WS_SUCCESS; @@ -1055,7 +1117,7 @@ static int wolfSSH_SFTP_SendName(WOLFSSH* ssh, WS_SFTPNAME* list, word32 count, WMEMCPY(out + idx, cur->fName, cur->fSz); idx += cur->fSz; c32toa(cur->lSz, out + idx); idx += UINT32_SZ; WMEMCPY(out + idx, cur->lName, cur->lSz); idx += cur->lSz; - if (SFTP_SetAtributes(ssh, out + idx, *outSz - idx, &cur->atrb) != + if (SFTP_SetAttributes(ssh, out + idx, *outSz - idx, &cur->atrb) != WS_SUCCESS) { return WS_FATAL_ERROR; } @@ -1086,6 +1148,7 @@ int wolfSSH_SFTP_RecvReadDir(WOLFSSH* ssh, int reqId, word32 maxSz) WS_SFTPNAME* list = NULL; word32 outSz = 0; DIR_HANDLE* cur = dirList; + char* dirName = NULL; if (ssh == NULL) { return WS_BAD_ARGUMENT; @@ -1111,6 +1174,7 @@ int wolfSSH_SFTP_RecvReadDir(WOLFSSH* ssh, int reqId, word32 maxSz) while (cur != NULL) { if (cur->id == handle) { dir = cur->dir; + dirName = cur->dirName; break; } cur = cur->next; @@ -1127,7 +1191,7 @@ int wolfSSH_SFTP_RecvReadDir(WOLFSSH* ssh, int reqId, word32 maxSz) outSz += UINT32_SZ + WOLFSSH_SFTP_HEADER; /* hold header+number of files */ do { name = wolfSSH_SFTPNAME_new(ssh->ctx->heap); - ret = wolfSSH_SFTPNAME_readdir(ssh, &dir, name); + ret = wolfSSH_SFTPNAME_readdir(ssh, &dir, name, dirName); if (ret == WS_SUCCESS || ret == WS_NEXT_ERROR) { count++; outSz += name->fSz + name->lSz + (UINT32_SZ * 2); @@ -1169,7 +1233,15 @@ int wolfSSH_SFTP_RecvReadDir(WOLFSSH* ssh, int reqId, word32 maxSz) return WS_FATAL_ERROR; } wolfSSH_SFTPNAME_list_free(list); - wolfSSH_stream_send(ssh, data, outSz); + + idx = 0; + while (idx < outSz) { + if ((ret = wolfSSH_stream_send(ssh, data + idx, outSz - idx)) < 0) { + WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER); + return ret; + } + idx += ret; + } WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER); return WS_SUCCESS; @@ -1215,6 +1287,7 @@ int wolfSSH_SFTP_RecvCloseDir(WOLFSSH* ssh, byte* handle, word32 handleSz) /* case where node is at head of list */ if (pre == cur) { dirList = cur->next; + WFREE(cur->dirName, ssh->ctx->heap, DYNTYPE_SFTP); WFREE(cur, ssh->ctx->heap, DYNTYPE_SFTP); } else { @@ -1225,6 +1298,7 @@ int wolfSSH_SFTP_RecvCloseDir(WOLFSSH* ssh, byte* handle, word32 handleSz) } else { pre->next = cur->next; + WFREE(cur->dirName, ssh->ctx->heap, DYNTYPE_SFTP); WFREE(cur, ssh->ctx->heap, DYNTYPE_SFTP); } } @@ -1232,7 +1306,7 @@ int wolfSSH_SFTP_RecvCloseDir(WOLFSSH* ssh, byte* handle, word32 handleSz) return WS_SUCCESS; } -#endif /* NO_WOLFSSL_DIR */ +#endif /* NO_WOLFSSH_DIR */ /* Handles packet to write a file * @@ -1424,13 +1498,13 @@ int wolfSSH_SFTP_RecvClose(WOLFSSH* ssh, int reqId, word32 maxSz) return WS_BUFFER_E; } -#ifndef NO_WOLFSSL_DIR +#ifndef NO_WOLFSSH_DIR /* check if is a handle for a directory */ if (sz == sizeof(word64)) { ret = wolfSSH_SFTP_RecvCloseDir(ssh, data + idx, sz); } else -#endif /* NO_WOLFSSL_DIR */ +#endif /* NO_WOLFSSH_DIR */ if (sz == sizeof(WFD)) { WMEMSET((byte*)&fd, 0, sizeof(WFD)); WMEMCPY((byte*)&fd, data + idx, sz); @@ -1770,21 +1844,19 @@ int SFTP_GetAttributes(const char* fileName, WS_SFTP_FILEATRB* atr, byte link) /* get additional attributes */ { - byte atrib = 0; - if (NU_Get_Attributes(&atrib, fileName) == NU_SUCCESS) { - atr->flags |= WOLFSSH_FILEATRB_PERM; - if (atrib & ADIRENT) { - atr->per |= 0x4000; - } - else { - atr->per |= 0x8000; - } - if (atrib & ANORMAL) { - atr->per |= 0x755; - } - if (atrib & ARDONLY) { - atr->per |= 0x444; - } + byte atrib = stats.fattribute; + atr->flags |= WOLFSSH_FILEATRB_PERM; + if (atrib & ADIRENT) { + atr->per |= 0x41ED; /* 755 with directory */ + } + else { + atr->per |= 0x8000; + } + if ((atrib & 0x01) == ANORMAL) { + atr->per |= 0x1ED; /* octal 755 */ + } + if (atrib & ARDONLY) { + atr->per |= 0x124; /* octal 444 */ } } @@ -1826,22 +1898,19 @@ int SFTP_GetAttributes_Handle(byte* handle, int handleSz, WS_SFTP_FILEATRB* atr) atr->sz = (word64)stats.fsize; { - byte atrib = 0; - if (NU_Get_Attributes(&atrib, cur->name) == NU_SUCCESS) { - atr->flags |= WOLFSSH_FILEATRB_PERM; - if (atrib & ADIRENT) { - atr->per |= 0x4000; - } - else { - atr->per |= 0x8000; - } - if (atrib & ANORMAL) { - atr->per |= 0x755; - } - - if (atrib & ARDONLY) { - atr->per |= 0x444; - } + byte atrib = stats.fattribute; + atr->flags |= WOLFSSH_FILEATRB_PERM; + if (atrib & ADIRENT) { + atr->per |= 0x41ED; /* 755 with directory */ + } + else { + atr->per |= 0x8000; + } + if ((atrib & 0x01) == ANORMAL) { + atr->per |= 0x1ED; /* octal 755 */ + } + if (atrib & ARDONLY) { + atr->per |= 0x124; /* octal 444 */ } } /* @TODO handle attribute extensions */ @@ -1993,7 +2062,7 @@ int wolfSSH_SFTP_RecvFSTAT(WOLFSSH* ssh, int reqId, word32 maxSz) WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER); return WS_FATAL_ERROR; } - SFTP_SetAtributes(ssh, data + WOLFSSH_SFTP_HEADER, sz, &atr); + SFTP_SetAttributes(ssh, data + WOLFSSH_SFTP_HEADER, sz, &atr); if (wolfSSH_stream_send(ssh, data, sz + WOLFSSH_SFTP_HEADER) < 0) { WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER); return WS_FATAL_ERROR; @@ -2067,7 +2136,7 @@ int wolfSSH_SFTP_RecvSTAT(WOLFSSH* ssh, int reqId, word32 maxSz) WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER); return WS_FATAL_ERROR; } - SFTP_SetAtributes(ssh, data + WOLFSSH_SFTP_HEADER, sz, &atr); + SFTP_SetAttributes(ssh, data + WOLFSSH_SFTP_HEADER, sz, &atr); if (wolfSSH_stream_send(ssh, data, sz + WOLFSSH_SFTP_HEADER) < 0) { WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER); return WS_FATAL_ERROR; @@ -2147,7 +2216,7 @@ int wolfSSH_SFTP_RecvLSTAT(WOLFSSH* ssh, int reqId, word32 maxSz) WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER); return WS_FATAL_ERROR; } - SFTP_SetAtributes(ssh, data + WOLFSSH_SFTP_HEADER, sz, &atr); + SFTP_SetAttributes(ssh, data + WOLFSSH_SFTP_HEADER, sz, &atr); if (wolfSSH_stream_send(ssh, data, sz + WOLFSSH_SFTP_HEADER) < 0) { WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER); return WS_FATAL_ERROR; @@ -2157,6 +2226,125 @@ int wolfSSH_SFTP_RecvLSTAT(WOLFSSH* ssh, int reqId, word32 maxSz) return WS_SUCCESS; } + +/* Set the files mode + * return WS_SUCCESS on success */ +static int SFTP_SetMode(WOLFSSH* ssh, char* name, word32 mode) { + (void)ssh; + if (WCHMOD(name, mode) != 0) { + return WS_BAD_FILE_E; + } + return WS_SUCCESS; +} + + +/* sets a files attributes + * returns WS_SUCCESS on success */ +static int SFTP_SetFileAttributes(WOLFSSH* ssh, char* name, WS_SFTP_FILEATRB* atr) +{ + int ret = WS_SUCCESS; + + /* check if size attribute present */ + if (atr->flags & WOLFSSH_FILEATRB_SIZE) { + /* @TODO set file size */ + } + + /* check if uid and gid attribute present */ + if (atr->flags & WOLFSSH_FILEATRB_UIDGID) { + /* @TODO set group and user id */ + } + + /* check if permissions attribute present */ + if (atr->flags & WOLFSSH_FILEATRB_PERM) { + ret = SFTP_SetMode(ssh, name, atr->per); + } + + /* check if time attribute present */ + if (atr->flags & WOLFSSH_FILEATRB_TIME) { + /* @TODO set time */ + } + + /* check if extended attributes are present */ + if (atr->flags & WOLFSSH_FILEATRB_EXT) { + /* @TODO handle extensions */ + } + + (void)ssh; + return ret ; +} + + +/* Handles a packet sent to set attributes of path + * + * returns WS_SUCCESS on success + */ +int wolfSSH_SFTP_RecvSetSTAT(WOLFSSH* ssh, int reqId, word32 maxSz) +{ + WS_SFTP_FILEATRB atr; + char* name = NULL; + int ret; + + word32 sz; + byte* data; + word32 idx = 0; + + if (ssh == NULL) { + return WS_BAD_ARGUMENT; + } + + WLOG(WS_LOG_SFTP, "Receiving WOLFSSH_FTP_SETSTAT"); + + data = (byte*)WMALLOC(maxSz, ssh->ctx->heap, DYNTYPE_BUFFER); + if (data == NULL) { + return WS_MEMORY_E; + } + if (wolfSSH_stream_read(ssh, data, maxSz) < 0) { + WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER); + return WS_FATAL_ERROR; + } + + ato32(data + idx, &sz); idx += UINT32_SZ; + if (sz + idx > maxSz) { + WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER); + return WS_BUFFER_E; + } + + /* plus one to make sure is null terminated */ + name = (char*)WMALLOC(sz + 1, ssh->ctx->heap, DYNTYPE_BUFFER); + if (name == NULL) { + WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER); + return WS_MEMORY_E; + } + WMEMCPY(name, data + idx, sz); idx += sz; + name[sz] = '\0'; + clean_path(name); + + if (SFTP_ParseAtributes_buffer(ssh, &atr, data + idx, maxSz - idx) != 0) { + printf("error parsing attributes\n"); + wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId, + "Unable to parse attributes error", "English"); + return WS_BAD_FILE_E; + } + WFREE(data, ssh->ctx->heap, DYNTYPE_BUFFER); + + /* try to set file attributes and send status back to client */ + if ((ret = SFTP_SetFileAttributes(ssh, name, &atr)) + != WS_SUCCESS) { + WFREE(name, ssh->ctx->heap, DYNTYPE_BUFFER); + + /* tell peer that was not ok */ + WLOG(WS_LOG_SFTP, "Unable to get set attributes of file/directory"); + wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_FAILURE, reqId, + "Unable to set attributes error", "English"); + return WS_BAD_FILE_E; + } + WFREE(name, ssh->ctx->heap, DYNTYPE_BUFFER); + + wolfSSH_SFTP_SendStatus(ssh, WOLFSSH_FTP_OK, reqId, "Set Attirbutes", + "English"); + + return WS_SUCCESS; +} #endif /* !NO_WOLFSSH_SERVER */ @@ -2402,7 +2590,7 @@ static int wolfSSH_SFTP_DoStatus(WOLFSSH* ssh, word32 reqId) } ato32(buf, &sz); - { + if (sz > 0) { byte* s = (byte*)WMALLOC(sz + 1, ssh->ctx->heap, DYNTYPE_BUFFER); if (s == NULL) { return WS_MEMORY_E; @@ -2426,8 +2614,7 @@ static int wolfSSH_SFTP_DoStatus(WOLFSSH* ssh, word32 reqId) } ato32(buf, &sz); - if (sz > 0) - { + if (sz > 0) { byte* s = (byte*)WMALLOC(sz + 1, ssh->ctx->heap, DYNTYPE_BUFFER); if (s == NULL) { return WS_MEMORY_E; @@ -2498,6 +2685,7 @@ int SFTP_ParseAtributes_buffer(WOLFSSH* ssh, WS_SFTP_FILEATRB* atr, byte* buf, word32 idx = 0; WMEMSET(atr, 0, sizeof(WS_SFTP_FILEATRB)); + /* get flags */ ato32(buf, &atr->flags); idx += UINT32_SZ; @@ -2914,6 +3102,36 @@ WS_SFTPNAME* wolfSSH_SFTP_LS(WOLFSSH* ssh, char* dir) } +/* Takes in an octal file permissions value and sets it to the file/directory + * returns WS_SUCCESS on success + */ +int wolfSSH_SFTP_CHMOD(WOLFSSH* ssh, char* n, char* oct) +{ + int ret; + int mode; + WS_SFTP_FILEATRB atr; + + if (ssh == NULL || n == NULL || oct == NULL) { + return WS_BAD_ARGUMENT; + } + + /* convert from octal to decimal */ + mode = wolfSSH_oct2dec(ssh, (byte*)oct, WSTRLEN(oct)); + if (mode < 0) { + return mode; + } + + /* get current attributes of path */ + if ((ret = wolfSSH_SFTP_STAT(ssh, n, &atr)) != WS_SUCCESS) { + return ret; + } + + /* update permissions */ + atr.per = mode; + return wolfSSH_SFTP_SetSTAT(ssh, n, &atr); +} + + /* helper function for common code between LSTAT and STAT * * returns WS_SUCCESS on success @@ -2997,6 +3215,69 @@ int wolfSSH_SFTP_LSTAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr) } +/* Sends packet to set the attributes of path + * + * dir NULL terminated string of path + * atr structure holding file attributes to use + * + * returns WS_SUCCESS on success + */ +int wolfSSH_SFTP_SetSTAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr) +{ + int dirSz, atrSz, status; + word32 maxSz, reqId; + byte type; + byte* data; + word32 idx; + + WLOG(WS_LOG_SFTP, "Entering wolfSSH_SFTP_SetSTAT()"); + if (ssh == NULL || dir == NULL || atr == NULL) { + return WS_BAD_ARGUMENT; + } + + dirSz = (int)WSTRLEN(dir); + atrSz = SFTP_AtributesSz(ssh, atr); + data = (byte*)WMALLOC(dirSz + atrSz + WOLFSSH_SFTP_HEADER + UINT32_SZ, + ssh->ctx->heap, DYNTYPE_BUFFER); + if (data == NULL) { + return WS_MEMORY_E; + } + + if (SFTP_SetHeader(ssh, ssh->reqId, WOLFSSH_FTP_SETSTAT, + dirSz + atrSz + UINT32_SZ, data) != WS_SUCCESS) { + WFREE(data, NULL, DYNTYPE_BUFFER); + return WS_FATAL_ERROR; + } + + idx = WOLFSSH_SFTP_HEADER; + c32toa(dirSz, data + idx); idx += UINT32_SZ; + WMEMCPY(data + idx, (byte*)dir, dirSz); idx += dirSz; + SFTP_SetAttributes(ssh, data + idx, atrSz, atr); idx += atrSz; + + /* send header and type specific data */ + if (wolfSSH_stream_send(ssh, data, idx) < 0) { + WFREE(data, NULL, DYNTYPE_BUFFER); + return WS_FATAL_ERROR; + } + WFREE(data, NULL, DYNTYPE_BUFFER); + + maxSz = SFTP_GetHeader(ssh, &reqId, &type); + if (type != WOLFSSH_FTP_STATUS) { + WLOG(WS_LOG_SFTP, "Unexpected packet type %d", type); + return WS_FATAL_ERROR; + } + else { + status = wolfSSH_SFTP_DoStatus(ssh, reqId); + if (status != WOLFSSH_FTP_OK) { + return WS_BAD_FILE_E; + } + } + (void)maxSz; + + return WS_SUCCESS; +} + + /* Open a file for reading/writing * * dir NULL terminated string holding file name diff --git a/wolfsftp/client/sftpclient.c b/wolfsftp/client/sftpclient.c index babb16cc..e4554b70 100644 --- a/wolfsftp/client/sftpclient.c +++ b/wolfsftp/client/sftpclient.c @@ -204,6 +204,7 @@ static void ShowCommands(void) { printf("\n\nCommands :\n"); printf("\tcd change directory\n"); + printf("\tchmod change mode\n"); printf("\tget pulls file(s) from server\n"); printf("\tls list current directory\n"); printf("\tmkdir creates new directory on server\n"); @@ -534,6 +535,61 @@ int doCmds() continue; } + if ((pt = WSTRNSTR(msg, "chmod", sizeof(msg))) != NULL) { + int sz; + char* f = NULL; + char mode[WOLFSSH_MAX_OCTET_LEN]; + + pt += sizeof("chmod"); + sz = (int)WSTRLEN(pt); + + if (pt[sz - 1] == '\n') pt[sz - 1] = '\0'; + + /* get mode */ + sz = (sz < WOLFSSH_MAX_OCTET_LEN - 1)? sz : + WOLFSSH_MAX_OCTET_LEN -1; + WMEMCPY(mode, pt, sz); + mode[WOLFSSH_MAX_OCTET_LEN - 1] = '\0'; + for (i = sz; i > 0; i--) { + if (mode[i] == ' ') { + mode[i] = '\0'; + break; + } + } + if (i == 0) { + printf("error with getting mode\r\n"); + continue; + } + pt += (int)WSTRLEN(mode); + sz = (int)WSTRLEN(pt); + for (i = 0; i < sz && pt[0] == ' '; i++, pt++); + + if (pt[0] != '/') { + int maxSz = (int)WSTRLEN(workingDir) + sz + 2; + f = XMALLOC(maxSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (f == NULL) { + err_sys("Error malloc'ing"); + } + + f[0] = '\0'; + WSTRNCAT(f, workingDir, maxSz); + if (WSTRLEN(workingDir) > 1) { + WSTRNCAT(f, "/", maxSz); + } + WSTRNCAT(f, pt, maxSz); + + pt = f; + } + + /* update permissions */ + if (wolfSSH_SFTP_CHMOD(ssh, pt, mode) != WS_SUCCESS) { + printf("unable to change path permissions\n"); + } + + XFREE(f, NULL, DYNAMIC_TYPE_TMP_BUFFER); + continue; + } + if ((pt = WSTRNSTR(msg, "rmdir", sizeof(msg))) != NULL) { int sz; char* f = NULL; diff --git a/wolfssh/internal.h b/wolfssh/internal.h index 7a4973a4..ed3574ff 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -662,6 +662,7 @@ WOLFSSH_LOCAL int wsScpSendCallback(WOLFSSH*, int, const char*, char*, word32, WOLFSSH_LOCAL void clean_path(char* path); WOLFSSH_LOCAL void DumpOctetString(const byte*, word32); +WOLFSSH_LOCAL int wolfSSH_oct2dec(WOLFSSH* ssh, byte* oct, word32 octSz); #ifdef __cplusplus diff --git a/wolfssh/port.h b/wolfssh/port.h index 7ce0e95e..7205c750 100644 --- a/wolfssh/port.h +++ b/wolfssh/port.h @@ -89,6 +89,35 @@ extern "C" { #define WCLOSE(fd) NU_Close((fd)) + static inline int wChmod(const char* f, int mode) { + unsigned char atr = 0; + + if (f == NULL) { + return -1; + } + + if (NU_Get_Attributes(&atr, f) != NU_SUCCESS) { + return -1; + } + + /* Set attribute value */ + atr = atr & 0xF0; /* clear first byte */ + if (mode == 0x124) { + atr |= ARDONLY; /* set read only value */ + } + else { + /* if not setting read only set to normal */ + atr |= ANORMAL; + } + + if (NU_Set_Attributes(f, atr) != NU_SUCCESS) { + return -1; + } + else { + return 0; + } + } + #define WCHMOD(f,m) wChmod((f),(m)) #else #define WFILE FILE WOLFSSH_API int wfopen(WFILE**, const char*, const char*); @@ -102,6 +131,7 @@ extern "C" { #define WREWIND(s) rewind((s)) #define WSEEK_END SEEK_END #define WUTIMES(f,t) utimes((f),(t)) + #define WCHMOD(f,m) chmod((f),(m)) #if (defined(WOLFSSH_SCP) || defined(WOLFSSH_SFTP)) && \ !defined(WOLFSSH_SCP_USER_CALLBACKS) && \ @@ -247,7 +277,7 @@ extern "C" { } #define WUTIMES(f,t) wUtimes((f), (t)) - #ifndef NO_WOLFSSL_DIR + #ifndef NO_WOLFSSH_DIR #define WDIR DSTAT #ifndef WOPENDIR @@ -310,7 +340,7 @@ extern "C" { #define WCLOSEDIR(d) NU_Done((d)) #define WREADDIR(d) (NU_Get_Next((d)) == NU_SUCCESS)?(d):NULL - #endif /* NO_WOLFSSL_DIR */ + #endif /* NO_WOLFSSH_DIR */ #else #include /* used for rmdir */ #include /* used for mkdir, stat, and lstat */ @@ -340,7 +370,7 @@ extern "C" { #define WPWRITE(fd,b,s,o) pwrite((fd),(b),(s),(o)) #define WPREAD(fd,b,s,o) pread((fd),(b),(s),(o)) -#ifndef NO_WOLFSSL_DIR +#ifndef NO_WOLFSSH_DIR #include /* used for opendir, readdir, and closedir */ #define WDIR DIR* @@ -348,7 +378,7 @@ extern "C" { #define WOPENDIR(c,d) ((*(c) = opendir((d))) == NULL) #define WCLOSEDIR(d) closedir(*(d)) #define WREADDIR(d) readdir(*(d)) -#endif /* NO_WOLFSSL_DIR */ +#endif /* NO_WOLFSSH_DIR */ #endif #endif /* WOLFSSH_SFTP or WOLFSSH_SCP */ diff --git a/wolfssh/ssh.h b/wolfssh/ssh.h index 2c1dce12..da5eb5b3 100644 --- a/wolfssh/ssh.h +++ b/wolfssh/ssh.h @@ -220,6 +220,7 @@ enum WS_DisconnectReasonCodes { #ifndef WOLFSSH_MAX_FILENAME #define WOLFSSH_MAX_FILENAME 256 #endif +#define WOLFSSH_MAX_OCTET_LEN 6 #ifdef __cplusplus diff --git a/wolfssh/wolfsftp.h b/wolfssh/wolfsftp.h index cacf8b70..fe9611fc 100644 --- a/wolfssh/wolfsftp.h +++ b/wolfssh/wolfsftp.h @@ -169,6 +169,7 @@ WOLFSSH_API int wolfSSH_SFTP_SendWritePacket(WOLFSSH* ssh, byte* handle, word32 handleSz, word64 ofst, byte* out, word32 outSz); WOLFSSH_API int wolfSSH_SFTP_STAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr); WOLFSSH_API int wolfSSH_SFTP_LSTAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr); +WOLFSSH_API int wolfSSH_SFTP_SetSTAT(WOLFSSH* ssh, char* dir, WS_SFTP_FILEATRB* atr); WOLFSSH_API void wolfSSH_SFTPNAME_free(WS_SFTPNAME* n); WOLFSSH_API void wolfSSH_SFTPNAME_list_free(WS_SFTPNAME* n); @@ -190,6 +191,7 @@ WOLFSSH_API int wolfSSH_SFTP_RMDIR(WOLFSSH* ssh, char* dir); WOLFSSH_API int wolfSSH_SFTP_Rename(WOLFSSH* ssh, const char* old, const char* nw); WOLFSSH_API WS_SFTPNAME* wolfSSH_SFTP_LS(WOLFSSH* ssh, char* dir); +WOLFSSH_API int wolfSSH_SFTP_CHMOD(WOLFSSH* ssh, char* n, char* oct); typedef void(WS_STATUS_CB)(WOLFSSH*, long, char*); WOLFSSH_API int wolfSSH_SFTP_Get(WOLFSSH* ssh, char* from, char* to, @@ -215,14 +217,15 @@ WOLFSSH_LOCAL int wolfSSH_SFTP_RecvRemove(WOLFSSH* ssh, int reqId, word32 maxSz) WOLFSSH_LOCAL int wolfSSH_SFTP_RecvRename(WOLFSSH* ssh, int reqId, word32 maxSz); WOLFSSH_LOCAL int wolfSSH_SFTP_RecvSTAT(WOLFSSH* ssh, int reqId, word32 maxSz); WOLFSSH_LOCAL int wolfSSH_SFTP_RecvLSTAT(WOLFSSH* ssh, int reqId, word32 maxSz); +WOLFSSH_LOCAL int wolfSSH_SFTP_RecvSetSTAT(WOLFSSH* ssh, int reqId, word32 maxSz); WOLFSSH_LOCAL int wolfSSH_SFTP_RecvFSTAT(WOLFSSH* ssh, int reqId, word32 maxSz); -#ifndef NO_WOLFSSL_DIR +#ifndef NO_WOLFSSH_DIR WOLFSSH_LOCAL int wolfSSH_SFTP_RecvOpenDir(WOLFSSH* ssh, int reqId, word32 maxSz); WOLFSSH_LOCAL int wolfSSH_SFTP_RecvReadDir(WOLFSSH* ssh, int reqId, word32 maxSz); WOLFSSH_LOCAL int wolfSSH_SFTP_RecvCloseDir(WOLFSSH* ssh, byte* handle, word32 handleSz); -#endif /* NO_WOLFSSL_DIR */ +#endif /* NO_WOLFSSH_DIR */ WOLFSSL_LOCAL int SFTP_AddHandleNode(WOLFSSH* ssh, byte* handle, word32 handleSz, char* name); WOLFSSL_LOCAL int SFTP_RemoveHandleNode(WOLFSSH* ssh, byte* handle, word32 handleSz);