mirror of
https://github.com/kingk85/uFTP.git
synced 2025-07-30 13:27:54 +03:00
fix: REST and PASS security fixes
This commit is contained in:
@ -305,9 +305,28 @@ static int processCommand(int processingElement, ftpDataType *ftpData)
|
||||
#define COMMAND_MAP_SIZE (sizeof(commandMap) / sizeof(CommandMapEntry))
|
||||
|
||||
int toReturn = 0;
|
||||
int commandIndex = -1;
|
||||
|
||||
//printTimeStamp();
|
||||
my_printf("\nCommand received from (%d): %s", processingElement, ftpData->clients[processingElement].theCommandReceived);
|
||||
|
||||
for (int i = 0; i < COMMAND_MAP_SIZE; ++i)
|
||||
{
|
||||
if (IS_CMD(ftpData->clients[processingElement].theCommandReceived, commandMap[i].command))
|
||||
{
|
||||
commandIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (commandIndex == -1)
|
||||
{
|
||||
toReturn = invalidCommandResponse(ftpData, processingElement);
|
||||
ftpData->clients[processingElement].commandIndex = 0;
|
||||
memset(ftpData->clients[processingElement].theCommandReceived, 0, CLIENT_COMMAND_STRING_SIZE+1);
|
||||
return toReturn;
|
||||
}
|
||||
|
||||
if(!isTransferCommand(processingElement, ftpData) &&
|
||||
ftpData->clients[processingElement].workerData.retrRestartAtByte != 0)
|
||||
{
|
||||
@ -332,15 +351,8 @@ static int processCommand(int processingElement, ftpDataType *ftpData)
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < COMMAND_MAP_SIZE; ++i)
|
||||
{
|
||||
if (IS_CMD(ftpData->clients[processingElement].theCommandReceived, commandMap[i].command))
|
||||
{
|
||||
my_printf("\n%s COMMAND RECEIVED", commandMap[i].command);
|
||||
toReturn = commandMap[i].handler(ftpData, processingElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
my_printf("\n%s COMMAND RECEIVED", commandMap[commandIndex].command);
|
||||
toReturn = commandMap[commandIndex].handler(ftpData, processingElement);
|
||||
|
||||
ftpData->clients[processingElement].commandIndex = 0;
|
||||
memset(ftpData->clients[processingElement].theCommandReceived, 0, CLIENT_COMMAND_STRING_SIZE+1);
|
||||
|
@ -167,6 +167,19 @@ int parseCommandPass(ftpDataType *data, int socketId)
|
||||
loginFailsDataType element;
|
||||
int searchPosition = -1;
|
||||
|
||||
if (data->clients[socketId].login.name.textLen <= 0)
|
||||
{
|
||||
returnCode = socketPrintf(data, socketId, "s", "503 Login with USER first.\r\n");
|
||||
|
||||
if (returnCode <= 0)
|
||||
{
|
||||
LOG_ERROR("socketPrintfError");
|
||||
return FTP_COMMAND_PROCESSED_WRITE_ERROR;
|
||||
}
|
||||
|
||||
return FTP_COMMAND_PROCESSED;
|
||||
}
|
||||
|
||||
thePass = getFtpCommandArg("PASS", data->clients[socketId].theCommandReceived, 0);
|
||||
|
||||
strncpy(element.ipAddress, data->clients[socketId].clientIpAddress, INET_ADDRSTRLEN);
|
||||
@ -1597,9 +1610,8 @@ int parseCommandMkd(ftpDataType *data, int socketId)
|
||||
{
|
||||
returnStatus = FILE_doChownFromUidGid(mkdFileName.text, data->clients[socketId].login.ownerShip.uid, data->clients[socketId].login.ownerShip.gid);
|
||||
}
|
||||
|
||||
returnCode = socketPrintf(data, socketId, "sss", "257 \"", theDirectoryFilename, "\" : The directory was successfully created\r\n");
|
||||
|
||||
returnCode = socketPrintf(data, socketId, "sss", "257 \"", theDirectoryFilename, "\" The directory was successfully created\r\n");
|
||||
my_printf("\n\n ------------------ Directory created");
|
||||
if (returnCode <= 0)
|
||||
{
|
||||
LOG_ERROR("socketPrintfError");
|
||||
@ -1882,6 +1894,21 @@ int notLoggedInMessage(ftpDataType *data, int socketId)
|
||||
return FTP_COMMAND_PROCESSED;
|
||||
}
|
||||
|
||||
int invalidCommandResponse(ftpDataType *data, int socketId)
|
||||
{
|
||||
int returnCode;
|
||||
returnCode = socketPrintf(data, socketId, "s", "500 Syntax error, command unrecognized.\r\n");
|
||||
|
||||
if (returnCode <= 0)
|
||||
{
|
||||
LOG_ERROR("socketPrintfError");
|
||||
return FTP_COMMAND_PROCESSED_WRITE_ERROR;
|
||||
}
|
||||
|
||||
return FTP_COMMAND_PROCESSED;
|
||||
}
|
||||
|
||||
|
||||
int parseCommandQuit(ftpDataType *data, int socketId)
|
||||
{
|
||||
int returnCode;
|
||||
|
@ -76,6 +76,7 @@ int parseCommandRetr(ftpDataType * data, int socketId);
|
||||
int parseCommandMkd(ftpDataType * data, int socketId);
|
||||
int parseCommandNoop(ftpDataType * data, int socketId);
|
||||
int notLoggedInMessage(ftpDataType * data, int socketId);
|
||||
int invalidCommandResponse(ftpDataType *data, int socketId);
|
||||
int parseCommandRmd(ftpDataType * data, int socketId);
|
||||
int parseCommandQuit(ftpDataType * data, int socketId);
|
||||
int parseCommandSize(ftpDataType * data, int socketId);
|
||||
|
@ -6,6 +6,7 @@ import time
|
||||
import ssl
|
||||
import socket
|
||||
import ftplib
|
||||
import re
|
||||
|
||||
FTP_HOST = '127.0.0.1'
|
||||
FTP_PORT = 21
|
||||
@ -115,32 +116,103 @@ class FTPServerRFCComplianceTests(unittest.TestCase):
|
||||
self.assertTrue(pwd.startswith('/'), f"PWD should return directory path, got: {pwd}")
|
||||
resp = self.ftp.cwd('/')
|
||||
self.assertTrue(resp.startswith('250'), f"CWD should succeed with 250 response, got: {resp}")
|
||||
|
||||
def Disabledtest_utf8_mkd(self):
|
||||
"""Verify server accepts MKD command with UTF-8 directory names."""
|
||||
|
||||
|
||||
def Disabledtest_mkdr(self):
|
||||
test_dirs = [
|
||||
"Café",
|
||||
"测试",
|
||||
"директория",
|
||||
"データ",
|
||||
"résumé"
|
||||
"dir1",
|
||||
"dir2",
|
||||
"dir3"
|
||||
]
|
||||
|
||||
|
||||
self.utf8_mkd(test_dirs)
|
||||
|
||||
|
||||
|
||||
def test_utf8_mkd(self):
|
||||
test_dirs = ["директория", "データ", "résumé"]
|
||||
|
||||
with ftplib.FTP() as ftp:
|
||||
ftp.connect(FTP_HOST, FTP_PORT, timeout=5)
|
||||
ftp.login(FTP_USER, FTP_PASS)
|
||||
|
||||
ftp.encoding = 'utf-8'
|
||||
|
||||
# Enable UTF-8 option
|
||||
try:
|
||||
ftp.sendcmd("OPTS UTF8 ON")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
for d in test_dirs:
|
||||
# Try to remove if exists
|
||||
try:
|
||||
ftp.rmd(d)
|
||||
except ftplib.error_perm:
|
||||
pass
|
||||
|
||||
response = ftp.mkd(d)
|
||||
|
||||
# Match response code and directory name more flexibly
|
||||
m = re.match(r'257\s+"?(.+?)"?\s', response)
|
||||
assert m is not None, f"MKD response format incorrect: {response}"
|
||||
dir_in_response = m.group(1)
|
||||
assert dir_in_response == d, f"Directory name mismatch: expected {d}, got {dir_in_response}"
|
||||
|
||||
# Cleanup
|
||||
ftp.rmd(d)
|
||||
|
||||
|
||||
def utf8_mkd(self, test_dirs):
|
||||
#test_dirs = [
|
||||
# "Café",
|
||||
# "测试",
|
||||
# "директория",
|
||||
# "データ",
|
||||
# "résumé"
|
||||
#]
|
||||
|
||||
with ftplib.FTP() as ftp:
|
||||
ftp.connect(FTP_HOST, FTP_PORT, timeout=5)
|
||||
ftp.login(FTP_USER, FTP_PASS)
|
||||
|
||||
ftp.encoding = 'utf-8'
|
||||
try:
|
||||
print("C: OPTS UTF8 ON")
|
||||
response = ftp.sendcmd("OPTS UTF8 ON")
|
||||
print(f"S: {repr(response)}")
|
||||
except Exception as e:
|
||||
print(f"Warning: OPTS UTF8 ON failed: {e}")
|
||||
|
||||
for dir_name_utf8 in test_dirs:
|
||||
with self.subTest(dir=dir_name_utf8):
|
||||
print(f"C: MKD {dir_name_utf8}")
|
||||
response = ftp.mkd(dir_name_utf8)
|
||||
print(f"S: {response}")
|
||||
|
||||
self.assertTrue(response.startswith('257'), f"MKD failed for directory '{dir_name_utf8}'")
|
||||
|
||||
# Clean up after test
|
||||
ftp.rmd(dir_name_utf8)
|
||||
print(f"Removed directory: {dir_name_utf8}")
|
||||
try:
|
||||
# Delete if exists
|
||||
try:
|
||||
ftp.cwd(dir_name_utf8)
|
||||
ftp.cwd("..")
|
||||
ftp.rmd(dir_name_utf8)
|
||||
print(f"Deleted existing dir: {dir_name_utf8}")
|
||||
except ftplib.error_perm:
|
||||
pass
|
||||
|
||||
# Create directory
|
||||
print(f"C: MKD {dir_name_utf8}")
|
||||
response = ftp.mkd(dir_name_utf8)
|
||||
print(f"S: {repr(response)}")
|
||||
|
||||
self.assertTrue(response.startswith('257'),
|
||||
f"MKD failed: Server responded with: {repr(response)}")
|
||||
|
||||
# Verify directory is accessible by changing to it
|
||||
ftp.cwd(dir_name_utf8)
|
||||
ftp.cwd("..")
|
||||
|
||||
# Cleanup
|
||||
ftp.rmd(dir_name_utf8)
|
||||
print(f"Removed directory: {dir_name_utf8}")
|
||||
|
||||
except Exception as e:
|
||||
self.fail(f"Exception for directory '{dir_name_utf8}': {e}")
|
||||
|
||||
|
||||
def test_pbsz_prot_violations(self):
|
||||
@ -193,36 +265,6 @@ class FTPServerRFCComplianceTests(unittest.TestCase):
|
||||
finally:
|
||||
ssock.close()
|
||||
|
||||
|
||||
|
||||
def Disabtest_utf8_mkd_with_opts(self):
|
||||
dir_name_utf8 = "rés"
|
||||
try:
|
||||
with ftplib.FTP() as ftp:
|
||||
ftp.connect(FTP_HOST, FTP_PORT, timeout=5)
|
||||
ftp.login(FTP_USER, FTP_PASS)
|
||||
|
||||
# Enable UTF-8 mode on the server
|
||||
print("C: OPTS UTF8 ON")
|
||||
response = ftp.sendcmd("OPTS UTF8 ON")
|
||||
print(f"S: {response}")
|
||||
|
||||
if not response.startswith('200'):
|
||||
print("Warning: OPTS UTF8 not supported or rejected by server.")
|
||||
|
||||
# Now try to create directory with UTF-8 name
|
||||
print(f"C: MKD {dir_name_utf8}")
|
||||
response = ftp.mkd(dir_name_utf8)
|
||||
print(f"S: {response}")
|
||||
|
||||
print(f"Successfully created UTF-8 directory: {dir_name_utf8}")
|
||||
|
||||
except ftplib.error_perm as e:
|
||||
print(f"FTP permission error creating directory '{dir_name_utf8}': {e}")
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
|
||||
|
||||
|
||||
def test_feat_space_indent(self):
|
||||
"""
|
||||
@ -630,7 +672,7 @@ class FTPServerRFCComplianceTests(unittest.TestCase):
|
||||
resp = self.ftp.quit()
|
||||
self.assertTrue(resp.startswith('221'), f"QUIT should respond with 221, got: {resp}")
|
||||
|
||||
def Disabledtest_invalid_command(self):
|
||||
def test_invalid_command(self):
|
||||
try:
|
||||
resp = self.ftp.sendcmd('FOOBAR')
|
||||
self.assertTrue(resp.startswith('500') or resp.startswith('502'),
|
||||
|
Reference in New Issue
Block a user