1 /* Copyright 2023 The BoringSSL Authors
2 *
3 * Permission to use, copy, modify, and/or distribute this software for any
4 * purpose with or without fee is hereby granted, provided that the above
5 * copyright notice and this permission notice appear in all copies.
6 *
7 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
14
15 #include "file_util.h"
16
17 #include <stdlib.h>
18
19 #if defined(OPENSSL_WINDOWS)
20 OPENSSL_MSVC_PRAGMA(warning(push, 3))
21 #include <windows.h>
OPENSSL_MSVC_PRAGMA(warning (pop))22 OPENSSL_MSVC_PRAGMA(warning(pop))
23 #else
24 #include <fcntl.h>
25 #include <sys/stat.h>
26 #include <unistd.h>
27 #endif
28
29 #include <openssl/rand.h>
30
31 #include "test_util.h"
32
33
34 BSSL_NAMESPACE_BEGIN
35
36 #if defined(OPENSSL_WINDOWS)
37 static void PrintLastError(const char *s) {
38 DWORD error = GetLastError();
39 char *buffer;
40 DWORD len = FormatMessageA(
41 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, 0, error, 0,
42 reinterpret_cast<char *>(&buffer), 0, nullptr);
43 std::string msg = "unknown error";
44 if (len > 0) {
45 msg.assign(buffer, len);
46 while (!msg.empty() && (msg.back() == '\r' || msg.back() == '\n')) {
47 msg.resize(msg.size() - 1);
48 }
49 }
50 LocalFree(buffer);
51 fprintf(stderr, "%s: %s (0x%lx)\n", s, msg.c_str(), error);
52 }
53 #endif // OPENSSL_WINDOWS
54
55 // GetTempDir returns the path to the temporary directory, or the empty string
56 // on error. On success, the result will include the directory separator.
GetTempDir()57 static std::string GetTempDir() {
58 #if defined(OPENSSL_WINDOWS)
59 char buf[MAX_PATH + 1];
60 DWORD len = GetTempPathA(sizeof(buf), buf);
61 return std::string(buf, len);
62 #else
63 const char *tmpdir = getenv("TMPDIR");
64 if (tmpdir != nullptr && *tmpdir != '\0') {
65 std::string ret = tmpdir;
66 if (ret.back() != '/') {
67 ret.push_back('/');
68 }
69 return ret;
70 }
71 #if defined(OPENSSL_ANDROID)
72 return "/data/local/tmp/";
73 #else
74 return "/tmp/";
75 #endif
76 #endif
77 }
78
SkipTempFileTests()79 bool SkipTempFileTests() {
80 #if defined(OPENSSL_ANDROID)
81 // When running in an APK context, /data/local/tmp is unreadable. Android
82 // versions before https://android-review.googlesource.com/c/1821337 do not
83 // set TMPDIR to a suitable replacement.
84 if (getenv("TMPDIR") == nullptr) {
85 static bool should_skip = [] {
86 TemporaryFile file;
87 return !file.Init();
88 }();
89 if (should_skip) {
90 fprintf(stderr, "Skipping tests with temporary files.\n");
91 return true;
92 }
93 }
94 #endif
95 return false;
96 }
97
~TemporaryFile()98 TemporaryFile::~TemporaryFile() {
99 #if defined(OPENSSL_WINDOWS)
100 if (!path_.empty() && !DeleteFileA(path_.c_str())) {
101 PrintLastError("Could not delete file");
102 }
103 #else
104 if (!path_.empty() && unlink(path_.c_str()) != 0) {
105 perror("Could not delete file");
106 }
107 #endif
108 }
109
Init(bssl::Span<const uint8_t> content)110 bool TemporaryFile::Init(bssl::Span<const uint8_t> content) {
111 std::string temp_dir = GetTempDir();
112 if (temp_dir.empty()) {
113 return false;
114 }
115
116 #if defined(OPENSSL_WINDOWS)
117 char path[MAX_PATH];
118 if (GetTempFileNameA(temp_dir.c_str(), "bssl",
119 /*uUnique=*/0, path) == 0) {
120 PrintLastError("Could not create temporary");
121 return false;
122 }
123 path_ = path;
124 #else
125 std::string path = temp_dir + "bssl_tmp_file.XXXXXX";
126 int fd = mkstemp(path.data());
127 if (fd < 0) {
128 perror("Could not create temporary file");
129 return false;
130 }
131 close(fd);
132 path_ = std::move(path);
133 #endif
134
135 ScopedFILE file = Open("wb");
136 if (file == nullptr) {
137 perror("Could not open temporary file");
138 return false;
139 }
140 if (!content.empty() &&
141 fwrite(content.data(), content.size(), /*nitems=*/1, file.get()) != 1) {
142 perror("Could not write temporary file");
143 return false;
144 }
145 return true;
146 }
147
Open(const char * mode) const148 ScopedFILE TemporaryFile::Open(const char *mode) const {
149 if (path_.empty()) {
150 return nullptr;
151 }
152 return ScopedFILE(fopen(path_.c_str(), mode));
153 }
154
OpenFD(int flags) const155 ScopedFD TemporaryFile::OpenFD(int flags) const {
156 if (path_.empty()) {
157 return ScopedFD();
158 }
159 #if defined(OPENSSL_WINDOWS)
160 return ScopedFD(_open(path_.c_str(), flags));
161 #else
162 return ScopedFD(open(path_.c_str(), flags));
163 #endif
164 }
165
~TemporaryDirectory()166 TemporaryDirectory::~TemporaryDirectory() {
167 if (path_.empty()) {
168 return;
169 }
170
171 #if defined(OPENSSL_WINDOWS)
172 for (const auto &file : files_) {
173 if (!DeleteFileA(GetFilePath(file).c_str())) {
174 PrintLastError("Could not delete file");
175 }
176 }
177 if (!RemoveDirectoryA(path_.c_str())) {
178 PrintLastError("Could not delete directory");
179 }
180 #else
181 for (const auto &file : files_) {
182 if (unlink(GetFilePath(file).c_str()) != 0) {
183 perror("Could not delete file");
184 }
185 }
186 if (rmdir(path_.c_str()) != 0) {
187 perror("Could not delete directory");
188 }
189 #endif
190 }
191
Init()192 bool TemporaryDirectory::Init() {
193 std::string path = GetTempDir();
194 if (path.empty()) {
195 return false;
196 }
197
198 #if defined(OPENSSL_WINDOWS)
199 // For simplicity, just try the first candidate and assume the directory
200 // doesn't exist. 128 bits of cryptographically secure randomness is plenty.
201 uint8_t buf[16];
202 RAND_bytes(buf, sizeof(buf));
203 path += "bssl_tmp_dir." + EncodeHex(buf);
204 if (!CreateDirectoryA(path.c_str(), /*lpSecurityAttributes=*/nullptr)) {
205 perror("Could not make temporary directory");
206 return false;
207 }
208 #else
209 path += "bssl_tmp_dir.XXXXXX";
210 if (mkdtemp(path.data()) == nullptr) {
211 perror("Could not make temporary directory");
212 return false;
213 }
214 #endif
215
216 path_ = std::move(path);
217 return true;
218 }
219
AddFile(const std::string & filename,bssl::Span<const uint8_t> content)220 bool TemporaryDirectory::AddFile(const std::string &filename,
221 bssl::Span<const uint8_t> content) {
222 if (path_.empty()) {
223 return false;
224 }
225
226 ScopedFILE file(fopen(GetFilePath(filename).c_str(), "wb"));
227 if (file == nullptr) {
228 perror("Could not open temporary file");
229 return false;
230 }
231 if (!content.empty() &&
232 fwrite(content.data(), content.size(), /*nitems=*/1, file.get()) != 1) {
233 perror("Could not write temporary file");
234 return false;
235 }
236
237 files_.insert(filename);
238 return true;
239 }
240
241 BSSL_NAMESPACE_END
242