Skip to content

Commit 5b10c3d

Browse files
asheshvdpage
authored andcommitted
Ensure the directory used for batch job step scripts is not predictable.
CVE-2025-0218 Per report from Wolfgang Frisch.
1 parent 20e5de2 commit 5b10c3d

3 files changed

Lines changed: 65 additions & 70 deletions

File tree

include/misc.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ std::wstring s2ws(const std::string &str);
2323
std::string ws2s(const std::wstring &wstr);
2424
#endif
2525
std::string generateRandomString(size_t length);
26-
std::string getTemporaryDirectoryPath();
26+
bool createUniqueTemporaryDirectory(const std::string &prefix, boost::filesystem::path &tempDir);
2727

2828
class MutexLocker
2929
{

job.cpp

Lines changed: 36 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -163,45 +163,41 @@ int Job::Execute()
163163
LOG_DEBUG
164164
);
165165

166-
// Get a temporary filename, then reuse it to create an empty directory.
167-
std::string sDirectory = getTemporaryDirectoryPath();
168-
std::string sFilesName = std::string("");
169-
std::string prefix = std::string("pga_");
170-
171-
// Generate random string of 6 characters long to make unique dir name
172-
std::string result = generateRandomString(7);
173-
sFilesName = prefix + m_jobid + std::string("_") + stepid + std::string("_") + result;
166+
namespace fs = boost::filesystem;
167+
168+
// Generate unique temporary directory
169+
std::string prefix = (
170+
boost::format("pga_%s_%s_") % m_jobid % stepid
171+
).str();
172+
173+
fs::path jobDir;
174+
fs::path filepath((
175+
boost::format("%s_%s.%s") %
176+
m_jobid % stepid %
174177
#if BOOST_OS_WINDOWS
175-
std::string sModel = (boost::format("%s\\%s") % sDirectory % sFilesName).str();
178+
".bat"
176179
#else
177-
std::string sModel = (boost::format("%s/%s") % sDirectory % sFilesName).str();
180+
".scr"
178181
#endif
179-
std::string dirname = sModel;
182+
).str());
183+
fs::path errorFilePath(
184+
(boost::format("%s_%s_error.txt") % m_jobid % stepid).str()
185+
);
180186

181-
if (dirname == "")
187+
if (!createUniqueTemporaryDirectory(prefix, jobDir))
182188
{
183189
output = "Couldn't get a temporary filename!";
184190
LogMessage(output, LOG_WARNING);
185191
rc = -1;
186-
break;
187-
}
188192

189-
if (!boost::filesystem::create_directory(boost::filesystem::path(dirname)))
190-
{
191-
LogMessage(
192-
"Couldn't create temporary directory: " + dirname, LOG_WARNING
193-
);
194-
rc = -1;
195193
break;
196194
}
197195

198-
#if BOOST_OS_WINDOWS
199-
std::string filename = dirname + "\\" + m_jobid + "_" + stepid + ".bat";
200-
std::string errorFile = dirname + "\\" + m_jobid + "_" + stepid + "_error.txt";
201-
#else
202-
std::string filename = dirname + "/" + m_jobid + "_" + stepid + ".scr";
203-
std::string errorFile = dirname + "/" + m_jobid + "_" + stepid + "_error.txt";
204-
#endif
196+
filepath = jobDir / filepath;
197+
errorFilePath = jobDir / errorFilePath;
198+
199+
std::string filename = filepath.string();
200+
std::string errorFile = errorFilePath.string();
205201

206202
std::string code = steps->GetString("jstcode");
207203

@@ -222,8 +218,8 @@ int Job::Execute()
222218
LOG_WARNING
223219
);
224220

225-
if (boost::filesystem::exists(dirname))
226-
boost::filesystem::remove_all(dirname);
221+
if (boost::filesystem::exists(jobDir))
222+
boost::filesystem::remove_all(jobDir);
227223

228224
rc = -1;
229225
break;
@@ -234,14 +230,17 @@ int Job::Execute()
234230
out_file.close();
235231

236232
#if !BOOST_OS_WINDOWS
237-
// change file permission to 700 for executable in linux
238-
int ret = chmod((const char *)filename.c_str(), S_IRWXU);
239-
240-
if (ret != 0)
233+
// Change file permission to 700 for executable in linux
234+
try {
235+
boost::filesystem::permissions(
236+
filepath, boost::filesystem::owner_all
237+
);
238+
} catch (const fs::filesystem_error &ex) {
241239
LogMessage(
242-
"Error setting executable permission to file: " + filename,
243-
LOG_DEBUG
240+
"Error setting executable permission to file: " +
241+
filename, LOG_DEBUG
244242
);
243+
}
245244
#endif
246245
}
247246

@@ -368,10 +367,8 @@ int Job::Execute()
368367
// output in the log, just throw warnings.
369368
try
370369
{
371-
boost::filesystem::path dir_path(dirname);
372-
373-
if (boost::filesystem::exists(dir_path))
374-
boost::filesystem::remove_all(dir_path);
370+
if (boost::filesystem::exists(jobDir))
371+
boost::filesystem::remove_all(jobDir);
375372
}
376373
catch (boost::filesystem::filesystem_error const & e)
377374
{

misc.cpp

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
#define APPVERSION_STR PGAGENT_VERSION
2323

24+
namespace fs = boost::filesystem;
25+
2426
// In unix.c or win32.c
2527
void usage(const std::string &executable);
2628

@@ -49,7 +51,7 @@ void printVersion()
4951
{
5052
printf("PostgreSQL Scheduling Agent\n");
5153
printf("Version: %s\n", APPVERSION_STR);
52-
}
54+
};
5355

5456
void setOptions(int argc, char **argv, const std::string &executable)
5557
{
@@ -192,41 +194,37 @@ std::string generateRandomString(size_t length)
192194
return result;
193195
}
194196

195-
std::string getTemporaryDirectoryPath()
197+
bool createUniqueTemporaryDirectory(const std::string &prefix, fs::path &uniqueDir)
196198
{
199+
const unsigned short MAX_ATTEMPTS = 100;
200+
unsigned short attempts = 0;
197201

198-
#if BOOST_OS_WINDOWS
199-
std::wstring tmp_dir;
202+
try {
203+
fs::path tempDir = fs::temp_directory_path();
200204

201-
wchar_t wcharPath[MAX_PATH];
205+
do {
206+
if (attempts++ >= MAX_ATTEMPTS)
207+
return false;
202208

203-
if (GetTempPathW(MAX_PATH, wcharPath))
204-
{
205-
tmp_dir = wcharPath;
209+
uniqueDir = tempDir / fs::unique_path(
210+
prefix + "%%%%%%%%-%%%%-%%%%-%%%%-%%%%%%%%%%%%"
211+
);
206212

207-
return ws2s(tmp_dir);
208-
}
209-
return "";
210-
#else
211-
// Read this environment variable (TMPDIR, TMP, TEMP, TEMPDIR) and if not found then use "/tmp"
212-
std::string tmp_dir = "/tmp";
213-
const char *s_tmp = getenv("TMPDIR");
213+
// Check if exists
214+
if (boost::filesystem::is_directory(uniqueDir))
215+
continue;
214216

215-
if (s_tmp != NULL)
216-
return s_tmp;
217-
218-
s_tmp = getenv("TMP");
219-
if (s_tmp != NULL)
220-
return s_tmp;
221-
222-
s_tmp = getenv("TEMP");
223-
if (s_tmp != NULL)
224-
return s_tmp;
217+
// Create the directory securely
218+
if (!fs::create_directory(uniqueDir)) {
219+
return false;
220+
}
225221

226-
s_tmp = getenv("TEMPDIR");
227-
if (s_tmp != NULL)
228-
return s_tmp;
222+
// Set appropriate permissions (example: owner read/write/execute only)
223+
fs::permissions(uniqueDir, boost::filesystem::owner_all);
229224

230-
return tmp_dir;
231-
#endif
225+
return true;
226+
} while (true);
227+
} catch (const fs::filesystem_error &ex) {
228+
return false;
229+
}
232230
}

0 commit comments

Comments
 (0)