Skip to content

Commit d72be1c

Browse files
martinhsvzimmerle
authored andcommitted
Fix: Only delete Multipart tmp files after rules have run
1 parent 1b7aa42 commit d72be1c

File tree

8 files changed

+255
-91
lines changed

8 files changed

+255
-91
lines changed

CHANGES

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
v3.x.y - YYYY-MMM-DD (to be released)
22
-------------------------------------
33

4+
- Fix: Only delete Multipart tmp files after rules have run
5+
[Issue #2427 - @martinhsv]
46
- Fixed MatchedVar on chained rules
57
[Issue #2423, #2435, #2436 - @michaelgranzow-avi]
68
- Add support for new operator rxGlobal

Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ TESTS+=test/test-cases/regression/issue-2000.json
159159
TESTS+=test/test-cases/regression/issue-2111.json
160160
TESTS+=test/test-cases/regression/issue-2196.json
161161
TESTS+=test/test-cases/regression/issue-2423-msg-in-chain.json
162+
TESTS+=test/test-cases/regression/issue-2427.json
162163
TESTS+=test/test-cases/regression/issue-394.json
163164
TESTS+=test/test-cases/regression/issue-849.json
164165
TESTS+=test/test-cases/regression/issue-960.json

headers/modsecurity/transaction.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ enum AllowType : int;
111111
namespace RequestBodyProcessor {
112112
class XML;
113113
class JSON;
114+
class MultipartPartTmpFile;
114115
}
115116
namespace operators {
116117
class Operator;
@@ -612,6 +613,8 @@ class Transaction : public TransactionAnchoredVariables, public TransactionSecMa
612613
std::string m_variableTimeWDay;
613614
std::string m_variableTimeYear;
614615

616+
std::vector<std::shared_ptr<RequestBodyProcessor::MultipartPartTmpFile>> m_multipartPartTmpFiles;
617+
615618
private:
616619
/**
617620
* Pointer to the callback function that will be called to fill

src/request_body_processor/multipart.cc

Lines changed: 84 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,61 @@ namespace RequestBodyProcessor {
3838
static const char* mime_charset_special = "!#$%&+-^_`{}~";
3939
static const char* attr_char_special = "!#$&+-.^_`~";
4040

41+
MultipartPartTmpFile::~MultipartPartTmpFile() {
42+
if (!m_tmp_file_name.empty() && m_delete) {
43+
/* make sure it is closed first */
44+
if (m_tmp_file_fd > 0) {
45+
Close();
46+
}
47+
48+
const int unlink_rc = unlink(m_tmp_file_name.c_str());
49+
if (unlink_rc < 0) {
50+
ms_dbg_a(m_transaction, 1, "Multipart: Failed to delete file (part) \"" \
51+
+ m_tmp_file_name + "\" because " \
52+
+ std::to_string(errno) + "(" \
53+
+ strerror(errno) + ")");
54+
} else {
55+
ms_dbg_a(m_transaction, 4, "Multipart: file deleted successfully (part) \"" \
56+
+ m_tmp_file_name + "\"");
57+
}
58+
59+
}
60+
}
61+
62+
void MultipartPartTmpFile::Open() {
63+
struct tm timeinfo;
64+
char tstr[300];
65+
time_t tt = time(NULL);
66+
67+
localtime_r(&tt, &timeinfo);
68+
69+
memset(tstr, '\0', 300);
70+
strftime(tstr, 299, "/%Y%m%d-%H%M%S", &timeinfo);
71+
72+
std::string path = m_transaction->m_rules->m_uploadDirectory.m_value;
73+
path = path + tstr + "-" + *m_transaction->m_id.get();
74+
path += "-file-XXXXXX";
75+
76+
char* tmp = strdup(path.c_str());
77+
m_tmp_file_fd = mkstemp(tmp);
78+
m_tmp_file_name.assign(tmp);
79+
free(tmp);
80+
ms_dbg_a(m_transaction, 4, "MultipartPartTmpFile: Create filename= " + m_tmp_file_name);
81+
82+
int mode = m_transaction->m_rules->m_uploadFileMode.m_value;
83+
if ((m_tmp_file_fd != -1) && (mode != 0)) {
84+
if (fchmod(m_tmp_file_fd, mode) == -1) {
85+
m_tmp_file_fd = -1;
86+
}
87+
}
88+
}
89+
90+
void MultipartPartTmpFile::Close() {
91+
close(m_tmp_file_fd);
92+
m_tmp_file_fd = -1;
93+
}
94+
95+
4196
Multipart::Multipart(const std::string &header, Transaction *transaction)
4297
: m_reqbody_no_files_length(0),
4398
m_nfiles(0),
@@ -80,30 +135,14 @@ Multipart::~Multipart() {
80135
if (m_transaction->m_rules->m_uploadKeepFiles
81136
!= RulesSetProperties::TrueConfigBoolean) {
82137
for (MultipartPart *m : m_parts) {
83-
if (m->m_type == MULTIPART_FILE) {
84-
if (!m->m_tmp_file_name.empty()) {
85-
/* make sure it is closed first */
86-
if (m->m_tmp_file_fd > 0) {
87-
close(m->m_tmp_file_fd);
88-
m->m_tmp_file_fd = -1;
89-
}
90-
const int unlink_rc =
91-
unlink(m->m_tmp_file_name.c_str());
92-
93-
if (unlink_rc < 0) {
94-
ms_dbg_a(m_transaction, 1,
95-
"Multipart: Failed to delete file (part) \"" \
96-
+ m->m_tmp_file_name + "\" because " \
97-
+ std::to_string(errno) + "(" \
98-
+ strerror(errno) + ")");
99-
} else {
100-
ms_dbg_a(m_transaction, 4,
101-
"Multipart: file deleted successfully (part) \"" \
102-
+ m->m_tmp_file_name + "\"");
103-
}
104-
105-
}
138+
if ((m->m_type == MULTIPART_FILE) && (m->m_tmp_file)) {
139+
// only mark for deletion for now; the file should stay on disk until
140+
// the transaction is complete
141+
ms_dbg_a(m_transaction, 9, "Multipart: Marking temporary file for deletion: " \
142+
+ m->m_tmp_file->getFilename());
143+
m->m_tmp_file->setDelete();
106144
}
145+
107146
}
108147
}
109148

@@ -432,7 +471,7 @@ int Multipart::parse_content_disposition(const char *c_d_value, int offset) {
432471
+ std::to_string(strlen(p)) + " bytes");
433472
m_flag_invalid_quoting = 1;
434473
}
435-
p++;
474+
/* p++; */
436475
return -12;
437476
}
438477
p++; /* move over the semi-colon */
@@ -453,40 +492,6 @@ int Multipart::parse_content_disposition(const char *c_d_value, int offset) {
453492
}
454493

455494

456-
int Multipart::tmp_file_name(std::string *filename) const {
457-
std::string path;
458-
struct tm timeinfo;
459-
char tstr[300];
460-
char *tmp;
461-
int fd;
462-
int mode;
463-
time_t tt = time(NULL);
464-
465-
localtime_r(&tt, &timeinfo);
466-
467-
path = m_transaction->m_rules->m_uploadDirectory.m_value;
468-
mode = m_transaction->m_rules->m_uploadFileMode.m_value;
469-
470-
memset(tstr, '\0', 300);
471-
strftime(tstr, 299, "/%Y%m%d-%H%M%S", &timeinfo);
472-
path = path + tstr + "-" + *m_transaction->m_id.get();
473-
path = path + "-file-XXXXXX";
474-
475-
tmp = strdup(path.c_str());
476-
477-
fd = mkstemp(tmp);
478-
filename->assign(tmp);
479-
free(tmp);
480-
if ((fd != -1) && (mode != 0)) {
481-
if (fchmod(fd, mode) == -1) {
482-
return -1;
483-
}
484-
}
485-
486-
return fd;
487-
}
488-
489-
490495
int Multipart::process_part_data(std::string *error, size_t offset) {
491496
char *p = m_buf + (MULTIPART_BUF_SIZE - m_bufleft);
492497
char localreserve[2] = { '\0', '\0' }; /* initialized to quiet warning */
@@ -546,20 +551,18 @@ int Multipart::process_part_data(std::string *error, size_t offset) {
546551
*/
547552
if (extract) {
548553
/* first create a temporary file if we don't have it already */
549-
if (m_mpp->m_tmp_file_fd == 0) {
550-
std::string path;
551-
m_mpp->m_tmp_file_fd = tmp_file_name(&path);
552-
553-
/* construct temporary file name */
554-
m_mpp->m_tmp_file_name = path;
554+
if (!m_mpp->m_tmp_file || !m_mpp->m_tmp_file->isValid()) {
555+
m_mpp->m_tmp_file = std::make_shared<RequestBodyProcessor::MultipartPartTmpFile>(m_transaction);
556+
m_transaction->m_multipartPartTmpFiles.push_back(m_mpp->m_tmp_file);
557+
m_mpp->m_tmp_file->Open();
555558

556559
/* do we have an opened file? */
557-
if (m_mpp->m_tmp_file_fd < 0) {
560+
if (!m_mpp->m_tmp_file || m_mpp->m_tmp_file->getFd() < 0) {
558561
ms_dbg_a(m_transaction, 1,
559562
"Multipart: Failed to create file: " \
560-
+ m_mpp->m_tmp_file_name);
563+
+ m_mpp->m_tmp_file->getFilename());
561564
error->assign("Multipart: Failed to create file: " \
562-
+ m_mpp->m_tmp_file_name);
565+
+ m_mpp->m_tmp_file->getFilename());
563566
return -1;
564567
}
565568
/* keep track of the files count */
@@ -569,18 +572,18 @@ int Multipart::process_part_data(std::string *error, size_t offset) {
569572
ms_dbg_a(m_transaction, 4,
570573
"Multipart: Created temporary file " \
571574
+ std::to_string(m_nfiles) + " (mode o" + std::to_string(m_transaction->m_rules->m_uploadFileMode.m_value) + "): " \
572-
+ m_mpp->m_tmp_file_name);
575+
+ m_mpp->m_tmp_file->getFilename());
573576
}
574577

575578
/* write the reserve first */
576579
if (m_reserve[0] != 0) {
577-
if (write(m_mpp->m_tmp_file_fd, &m_reserve[1], m_reserve[0])
580+
if (write(m_mpp->m_tmp_file->getFd(), &m_reserve[1], m_reserve[0])
578581
!= m_reserve[0]) {
579582
ms_dbg_a(m_transaction, 1,
580583
"Multipart: writing to \"" \
581-
+ m_mpp->m_tmp_file_name + "\" failed");
584+
+ m_mpp->m_tmp_file->getFilename() + "\" failed");
582585
error->assign("Multipart: writing to \"" \
583-
+ m_mpp->m_tmp_file_name + "\" failed");
586+
+ m_mpp->m_tmp_file->getFilename() + "\" failed");
584587
return -1;
585588
}
586589

@@ -594,14 +597,14 @@ int Multipart::process_part_data(std::string *error, size_t offset) {
594597

595598
/* write data to the file */
596599

597-
if (write(m_mpp->m_tmp_file_fd, m_buf,
600+
if (write(m_mpp->m_tmp_file->getFd(), m_buf,
598601
MULTIPART_BUF_SIZE - m_bufleft)
599602
!= (MULTIPART_BUF_SIZE - m_bufleft)) {
600603
ms_dbg_a(m_transaction, 1,
601604
"Multipart: writing to \"" \
602-
+ m_mpp->m_tmp_file_name + "\" failed");
605+
+ m_mpp->m_tmp_file->getFilename() + "\" failed");
603606
error->assign("Multipart: writing to \"" \
604-
+ m_mpp->m_tmp_file_name + "\" failed");
607+
+ m_mpp->m_tmp_file->getFilename() + "\" failed");
605608
return -1;
606609
}
607610

@@ -911,11 +914,9 @@ int Multipart::process_boundary(int last_part) {
911914
/* if there was a part being built finish it */
912915
if (m_mpp != NULL) {
913916
/* close the temp file */
914-
if ((m_mpp->m_type == MULTIPART_FILE)
915-
&& (!m_mpp->m_tmp_file_name.empty())
916-
&& (m_mpp->m_tmp_file_fd != 0)) {
917-
close(m_mpp->m_tmp_file_fd);
918-
m_mpp->m_tmp_file_fd = -1;
917+
if ((m_mpp->m_type == MULTIPART_FILE) && (m_mpp->m_tmp_file)
918+
&& (m_mpp->m_tmp_file->isValid())) {
919+
m_mpp->m_tmp_file->Close();
919920
}
920921

921922
if (m_mpp->m_type != MULTIPART_FILE) {
@@ -1128,8 +1129,10 @@ int Multipart::multipart_complete(std::string *error) {
11281129
if (m->m_type == MULTIPART_FILE) {
11291130
std::string tmp_name;
11301131
std::string name;
1131-
if (!m->m_tmp_file_name.empty()) {
1132-
tmp_name.assign(m->m_tmp_file_name);
1132+
if (m->m_tmp_file && !m->m_tmp_file->getFilename().empty()) {
1133+
tmp_name.assign(m->m_tmp_file->getFilename());
1134+
m_transaction->m_variableFilesTmpNames.set(m->m_tmp_file->getFilename(),
1135+
m->m_tmp_file->getFilename(), m->m_filenameOffset);
11331136
}
11341137
if (!m->m_filename.empty()) {
11351138
name.assign(m->m_filename);
@@ -1149,9 +1152,6 @@ int Multipart::multipart_complete(std::string *error) {
11491152
m_transaction->m_variableFilesTmpContent.set(m->m_filename,
11501153
m->m_value, m->m_valueOffset);
11511154

1152-
m_transaction->m_variableFilesTmpNames.set(m->m_tmp_file_name,
1153-
m->m_tmp_file_name, m->m_filenameOffset);
1154-
11551155
file_combined_size = file_combined_size + m->m_tmp_file_size.first;
11561156

11571157
m_transaction->m_variableFilesCombinedSize.set(

src/request_body_processor/multipart.h

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,38 @@ struct MyEqual {
5454
};
5555

5656

57+
class MultipartPartTmpFile {
58+
public:
59+
explicit MultipartPartTmpFile(Transaction *transaction)
60+
: m_transaction(transaction),
61+
m_tmp_file_fd(0),
62+
m_delete(false)
63+
{ }
64+
65+
~MultipartPartTmpFile();
66+
67+
// forbid copying
68+
MultipartPartTmpFile(const MultipartPartTmpFile&) = delete;
69+
MultipartPartTmpFile& operator=(const MultipartPartTmpFile&) = delete;
70+
71+
int getFd() const {return m_tmp_file_fd;}
72+
void setFd(int fd) {m_tmp_file_fd = fd;}
73+
const std::string& getFilename() const {return m_tmp_file_name;}
74+
void setDelete() {m_delete = true;}
75+
76+
bool isValid() const {return ((m_tmp_file_fd != 0) && (!m_tmp_file_name.empty()));}
77+
78+
void Open();
79+
void Close();
80+
81+
private:
82+
Transaction *m_transaction;
83+
int m_tmp_file_fd;
84+
std::string m_tmp_file_name;
85+
bool m_delete; // whether to delete when transaction is done
86+
};
87+
88+
5789
class MultipartPart {
5890
public:
5991
MultipartPart()
@@ -63,8 +95,6 @@ class MultipartPart {
6395
m_value(""),
6496
m_valueOffset(0),
6597
m_value_parts(),
66-
m_tmp_file_name(""),
67-
m_tmp_file_fd(0),
6898
m_tmp_file_size(),
6999
m_filename(""),
70100
m_filenameOffset(0),
@@ -98,8 +128,7 @@ class MultipartPart {
98128
/* std::string m_content_type; */
99129

100130
/* files only, the name of the temporary file holding data */
101-
std::string m_tmp_file_name;
102-
int m_tmp_file_fd;
131+
std::shared_ptr<RequestBodyProcessor::MultipartPartTmpFile> m_tmp_file;
103132
std::pair<size_t, size_t> m_tmp_file_size;
104133

105134
/* files only, filename as supplied by the browser */
@@ -133,8 +162,6 @@ class Multipart {
133162
int process_part_header(std::string *error, int offset);
134163
int process_part_data(std::string *error, size_t offset);
135164

136-
int tmp_file_name(std::string *filename) const;
137-
138165
void validate_quotes(const char *data);
139166

140167
size_t m_reqbody_no_files_length;

test/cppcheck_suppressions.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ invalidScanfArgType_int:src/rules_set_properties.cc:102
3535
unmatchedSuppression:src/utils/geo_lookup.cc:82
3636
useInitializationList:src/utils/shared_files.h:87
3737
unmatchedSuppression:src/utils/msc_tree.cc
38-
functionStatic:headers/modsecurity/transaction.h:404
38+
functionStatic:headers/modsecurity/transaction.h:405
3939
duplicateBranch:src/audit_log/audit_log.cc:223
4040
unreadVariable:src/request_body_processor/multipart.cc:435
4141
stlcstrParam:src/audit_log/writer/parallel.cc:145
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#!/usr/bin/lua
2+
3+
function main(filename)
4+
local file = io.open(filename, 'r')
5+
local chunk = file:read(1024)
6+
local ret = string.match(chunk, 'abcdef')
7+
io.close(file)
8+
9+
return ret
10+
end

0 commit comments

Comments
 (0)