]>
Commit | Line | Data |
---|---|---|
cfe95ada RG |
1 | /* |
2 | * This file is part of PowerDNS or dnsdist. | |
3 | * Copyright -- PowerDNS.COM B.V. and its contributors | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of version 2 of the GNU General Public License as | |
7 | * published by the Free Software Foundation. | |
8 | * | |
9 | * In addition, for the avoidance of any doubt, permission is granted to | |
10 | * link this program with OpenSSL and to (re)distribute the binaries | |
11 | * produced as the result of such linking. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License | |
19 | * along with this program; if not, write to the Free Software | |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
21 | */ | |
22 | #include "config.h" | |
23 | ||
8a6030b6 | 24 | #include <cmath> |
cfe95ada RG |
25 | #include <stdexcept> |
26 | ||
27 | #ifdef HAVE_LIBSODIUM | |
28 | #include <sodium.h> | |
29 | #endif | |
30 | ||
2f32819a | 31 | #if !defined(DISABLE_HASHED_CREDENTIALS) && defined(HAVE_EVP_PKEY_CTX_SET1_SCRYPT_SALT) |
8a6030b6 RG |
32 | #include <openssl/evp.h> |
33 | #include <openssl/kdf.h> | |
8535f666 | 34 | #include <openssl/opensslv.h> |
8a6030b6 RG |
35 | #include <openssl/rand.h> |
36 | #endif | |
37 | ||
061bc5f0 RG |
38 | #include <fcntl.h> |
39 | #include <sys/stat.h> | |
40 | #include <unistd.h> | |
41 | ||
8a6030b6 | 42 | #include "base64.hh" |
cfe95ada | 43 | #include "credentials.hh" |
b2504b29 | 44 | #include "misc.hh" |
cfe95ada | 45 | |
2f32819a | 46 | #if !defined(DISABLE_HASHED_CREDENTIALS) && defined(HAVE_EVP_PKEY_CTX_SET1_SCRYPT_SALT) |
8a6030b6 RG |
47 | static size_t const pwhash_max_size = 128U; /* maximum size of the output */ |
48 | static size_t const pwhash_output_size = 32U; /* size of the hashed output (before base64 encoding) */ | |
49 | static unsigned int const pwhash_salt_size = 16U; /* size of the salt (before base64 encoding */ | |
31ec1c9f | 50 | static uint64_t const pwhash_max_work_factor = 32768U; /* max N for interactive login purposes */ |
8a6030b6 RG |
51 | |
52 | /* PHC string format, storing N as log2(N) as done by passlib. | |
53 | for now we only support one algo but we might have to change that later */ | |
54 | static std::string const pwhash_prefix = "$scrypt$"; | |
55 | static size_t const pwhash_prefix_size = pwhash_prefix.size(); | |
56 | #endif | |
57 | ||
58 | uint64_t const CredentialsHolder::s_defaultWorkFactor{1024U}; /* N */ | |
59 | uint64_t const CredentialsHolder::s_defaultParallelFactor{1U}; /* p */ | |
60 | uint64_t const CredentialsHolder::s_defaultBlockSize{8U}; /* r */ | |
61 | ||
31ec1c9f RG |
62 | SensitiveData::SensitiveData(std::string&& data) : |
63 | d_data(std::move(data)) | |
8a6030b6 RG |
64 | { |
65 | #ifdef HAVE_LIBSODIUM | |
66 | sodium_mlock(d_data.data(), d_data.size()); | |
67 | #endif | |
68 | } | |
69 | ||
0bc984f9 RG |
70 | SensitiveData& SensitiveData::operator=(SensitiveData&& rhs) |
71 | { | |
72 | d_data = std::move(rhs.d_data); | |
73 | return *this; | |
74 | } | |
75 | ||
8a6030b6 RG |
76 | SensitiveData::SensitiveData(size_t bytes) |
77 | { | |
78 | d_data.resize(bytes); | |
79 | #ifdef HAVE_LIBSODIUM | |
80 | sodium_mlock(d_data.data(), d_data.size()); | |
81 | #endif | |
82 | } | |
83 | ||
84 | SensitiveData::~SensitiveData() | |
85 | { | |
86 | clear(); | |
87 | } | |
88 | ||
89 | void SensitiveData::clear() | |
90 | { | |
91 | #ifdef HAVE_LIBSODIUM | |
92 | sodium_munlock(d_data.data(), d_data.size()); | |
93 | #endif | |
94 | d_data.clear(); | |
95 | } | |
96 | ||
8a6030b6 RG |
97 | static std::string hashPasswordInternal(const std::string& password, const std::string& salt, uint64_t workFactor, uint64_t parallelFactor, uint64_t blockSize) |
98 | { | |
2f32819a | 99 | #if !defined(DISABLE_HASHED_CREDENTIALS) && defined(HAVE_EVP_PKEY_CTX_SET1_SCRYPT_SALT) |
31ec1c9f | 100 | auto pctx = std::unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX*)>(EVP_PKEY_CTX_new_id(EVP_PKEY_SCRYPT, nullptr), EVP_PKEY_CTX_free); |
8a6030b6 RG |
101 | if (!pctx) { |
102 | throw std::runtime_error("Error getting a scrypt context to hash the supplied password"); | |
103 | } | |
104 | ||
105 | if (EVP_PKEY_derive_init(pctx.get()) <= 0) { | |
106 | throw std::runtime_error("Error intializing the scrypt context to hash the supplied password"); | |
107 | } | |
108 | ||
8535f666 RG |
109 | // OpenSSL 3.0 changed the string arg to const unsigned char*, other versions use const char * |
110 | #if OPENSSL_VERSION_MAJOR >= 3 | |
111 | auto passwordData = reinterpret_cast<const char*>(password.data()); | |
112 | #else | |
113 | auto passwordData = reinterpret_cast<const unsigned char*>(password.data()); | |
114 | #endif | |
115 | if (EVP_PKEY_CTX_set1_pbe_pass(pctx.get(), passwordData, password.size()) <= 0) { | |
8a6030b6 | 116 | throw std::runtime_error("Error adding the password to the scrypt context to hash the supplied password"); |
31ec1c9f | 117 | } |
8a6030b6 | 118 | |
810cb195 | 119 | if (EVP_PKEY_CTX_set1_scrypt_salt(pctx.get(), reinterpret_cast<const unsigned char*>(salt.data()), salt.size()) <= 0) { |
8a6030b6 RG |
120 | throw std::runtime_error("Error adding the salt to the scrypt context to hash the supplied password"); |
121 | } | |
122 | ||
123 | if (EVP_PKEY_CTX_set_scrypt_N(pctx.get(), workFactor) <= 0) { | |
124 | throw std::runtime_error("Error setting the work factor to the scrypt context to hash the supplied password"); | |
125 | } | |
126 | ||
127 | if (EVP_PKEY_CTX_set_scrypt_r(pctx.get(), blockSize) <= 0) { | |
128 | throw std::runtime_error("Error setting the block size to the scrypt context to hash the supplied password"); | |
129 | } | |
130 | ||
131 | if (EVP_PKEY_CTX_set_scrypt_p(pctx.get(), parallelFactor) <= 0) { | |
132 | throw std::runtime_error("Error setting the parallel factor to the scrypt context to hash the supplied password"); | |
133 | } | |
134 | ||
135 | std::string out; | |
136 | out.resize(pwhash_output_size); | |
137 | size_t outlen = out.size(); | |
138 | ||
139 | if (EVP_PKEY_derive(pctx.get(), reinterpret_cast<unsigned char*>(out.data()), &outlen) <= 0 || outlen != pwhash_output_size) { | |
140 | throw std::runtime_error("Error deriving the output from the scrypt context to hash the supplied password"); | |
141 | } | |
142 | ||
143 | return out; | |
b59e6712 RG |
144 | #else |
145 | throw std::runtime_error("Hashing support is not available"); | |
8a6030b6 | 146 | #endif |
b59e6712 | 147 | } |
8a6030b6 RG |
148 | |
149 | static std::string generateRandomSalt() | |
150 | { | |
2f32819a | 151 | #if !defined(DISABLE_HASHED_CREDENTIALS) && defined(HAVE_EVP_PKEY_CTX_SET1_SCRYPT_SALT) |
8a6030b6 RG |
152 | /* generate a random salt */ |
153 | std::string salt; | |
154 | salt.resize(pwhash_salt_size); | |
155 | ||
156 | if (RAND_bytes(reinterpret_cast<unsigned char*>(salt.data()), salt.size()) != 1) { | |
157 | throw std::runtime_error("Error while generating a salt to hash the supplied password"); | |
158 | } | |
159 | ||
160 | return salt; | |
161 | #else | |
162 | throw std::runtime_error("Generating a salted password requires scrypt support in OpenSSL, and it is not available"); | |
163 | #endif | |
164 | } | |
165 | ||
4ec3ff03 | 166 | std::string hashPassword(const std::string& password, uint64_t workFactor, uint64_t parallelFactor, uint64_t blockSize) |
cfe95ada | 167 | { |
2f32819a RG |
168 | #if !defined(DISABLE_HASHED_CREDENTIALS) && defined(HAVE_EVP_PKEY_CTX_SET1_SCRYPT_SALT) |
169 | if (workFactor == 0) { | |
170 | throw std::runtime_error("Invalid work factor of " + std::to_string(workFactor) + " passed to hashPassword()"); | |
171 | } | |
172 | ||
cfe95ada | 173 | std::string result; |
8a6030b6 | 174 | result.reserve(pwhash_max_size); |
cfe95ada | 175 | |
8a6030b6 RG |
176 | result.append(pwhash_prefix); |
177 | result.append("ln="); | |
4ec3ff03 | 178 | result.append(std::to_string(static_cast<uint64_t>(std::log2(workFactor)))); |
8a6030b6 | 179 | result.append(",p="); |
4ec3ff03 | 180 | result.append(std::to_string(parallelFactor)); |
8a6030b6 | 181 | result.append(",r="); |
4ec3ff03 | 182 | result.append(std::to_string(blockSize)); |
8a6030b6 RG |
183 | result.append("$"); |
184 | auto salt = generateRandomSalt(); | |
185 | result.append(Base64Encode(salt)); | |
186 | result.append("$"); | |
187 | ||
4ec3ff03 | 188 | auto out = hashPasswordInternal(password, salt, workFactor, parallelFactor, blockSize); |
8a6030b6 RG |
189 | |
190 | result.append(Base64Encode(out)); | |
cfe95ada RG |
191 | |
192 | return result; | |
193 | #else | |
8a6030b6 RG |
194 | throw std::runtime_error("Hashing a password requires scrypt support in OpenSSL, and it is not available"); |
195 | #endif | |
196 | } | |
197 | ||
4ec3ff03 RG |
198 | std::string hashPassword(const std::string& password) |
199 | { | |
2f32819a | 200 | #if !defined(DISABLE_HASHED_CREDENTIALS) && defined(HAVE_EVP_PKEY_CTX_SET1_SCRYPT_SALT) |
4ec3ff03 RG |
201 | return hashPassword(password, CredentialsHolder::s_defaultWorkFactor, CredentialsHolder::s_defaultParallelFactor, CredentialsHolder::s_defaultBlockSize); |
202 | #else | |
203 | throw std::runtime_error("Hashing a password requires scrypt support in OpenSSL, and it is not available"); | |
204 | #endif | |
205 | } | |
206 | ||
8a6030b6 RG |
207 | bool verifyPassword(const std::string& binaryHash, const std::string& salt, uint64_t workFactor, uint64_t parallelFactor, uint64_t blockSize, const std::string& binaryPassword) |
208 | { | |
2f32819a | 209 | #if !defined(DISABLE_HASHED_CREDENTIALS) && defined(HAVE_EVP_PKEY_CTX_SET1_SCRYPT_SALT) |
8a6030b6 RG |
210 | auto expected = hashPasswordInternal(binaryPassword, salt, workFactor, parallelFactor, blockSize); |
211 | return constantTimeStringEquals(expected, binaryHash); | |
212 | #else | |
213 | throw std::runtime_error("Hashing a password requires scrypt support in OpenSSL, and it is not available"); | |
214 | #endif | |
215 | } | |
216 | ||
217 | /* parse a hashed password in PHC string format */ | |
218 | static void parseHashed(const std::string& hash, std::string& salt, std::string& hashedPassword, uint64_t& workFactor, uint64_t& parallelFactor, uint64_t& blockSize) | |
219 | { | |
2f32819a | 220 | #if !defined(DISABLE_HASHED_CREDENTIALS) && defined(HAVE_EVP_PKEY_CTX_SET1_SCRYPT_SALT) |
8a6030b6 RG |
221 | auto parametersEnd = hash.find('$', pwhash_prefix.size()); |
222 | if (parametersEnd == std::string::npos || parametersEnd == hash.size()) { | |
8a6030b6 RG |
223 | throw std::runtime_error("Invalid hashed password format, no parameters"); |
224 | } | |
225 | ||
226 | auto parametersStr = hash.substr(pwhash_prefix.size(), parametersEnd); | |
227 | std::vector<std::string> parameters; | |
228 | parameters.reserve(3); | |
229 | stringtok(parameters, parametersStr, ","); | |
230 | if (parameters.size() != 3) { | |
231 | throw std::runtime_error("Invalid hashed password format, expecting 3 parameters, got " + std::to_string(parameters.size())); | |
232 | } | |
233 | ||
234 | if (!boost::starts_with(parameters.at(0), "ln=")) { | |
235 | throw std::runtime_error("Invalid hashed password format, ln= parameter not found"); | |
236 | } | |
237 | ||
238 | if (!boost::starts_with(parameters.at(1), "p=")) { | |
239 | throw std::runtime_error("Invalid hashed password format, p= parameter not found"); | |
240 | } | |
241 | ||
242 | if (!boost::starts_with(parameters.at(2), "r=")) { | |
243 | throw std::runtime_error("Invalid hashed password format, r= parameter not found"); | |
244 | } | |
245 | ||
246 | auto saltPos = parametersEnd + 1; | |
247 | auto saltEnd = hash.find('$', saltPos); | |
248 | if (saltEnd == std::string::npos || saltEnd == hash.size()) { | |
249 | throw std::runtime_error("Invalid hashed password format"); | |
250 | } | |
251 | ||
252 | try { | |
a0383aad | 253 | workFactor = pdns::checked_stoi<uint64_t>(parameters.at(0).substr(3)); |
0ef92c6c | 254 | workFactor = static_cast<uint64_t>(1) << workFactor; |
8a6030b6 RG |
255 | if (workFactor > pwhash_max_work_factor) { |
256 | throw std::runtime_error("Invalid work factor of " + std::to_string(workFactor) + " in hashed password string, maximum is " + std::to_string(pwhash_max_work_factor)); | |
257 | } | |
258 | ||
a0383aad FM |
259 | parallelFactor = pdns::checked_stoi<uint64_t>(parameters.at(1).substr(2)); |
260 | blockSize = pdns::checked_stoi<uint64_t>(parameters.at(2).substr(2)); | |
8a6030b6 RG |
261 | |
262 | auto b64Salt = hash.substr(saltPos, saltEnd - saltPos); | |
263 | salt.reserve(pwhash_salt_size); | |
264 | B64Decode(b64Salt, salt); | |
265 | ||
266 | if (salt.size() != pwhash_salt_size) { | |
267 | throw std::runtime_error("Invalid salt in hashed password string"); | |
268 | } | |
269 | ||
270 | hashedPassword.reserve(pwhash_output_size); | |
271 | B64Decode(hash.substr(saltEnd + 1), hashedPassword); | |
272 | ||
273 | if (hashedPassword.size() != pwhash_output_size) { | |
274 | throw std::runtime_error("Invalid hash in hashed password string"); | |
275 | } | |
276 | } | |
277 | catch (const std::exception& e) { | |
278 | throw std::runtime_error("Invalid hashed password format, unable to parse parameters"); | |
279 | } | |
cfe95ada RG |
280 | #endif |
281 | } | |
282 | ||
283 | bool verifyPassword(const std::string& hash, const std::string& password) | |
284 | { | |
8a6030b6 RG |
285 | if (!isPasswordHashed(hash)) { |
286 | return false; | |
cfe95ada RG |
287 | } |
288 | ||
2f32819a | 289 | #if !defined(DISABLE_HASHED_CREDENTIALS) && defined(HAVE_EVP_PKEY_CTX_SET1_SCRYPT_SALT) |
8a6030b6 RG |
290 | std::string salt; |
291 | std::string hashedPassword; | |
292 | uint64_t workFactor = 0; | |
293 | uint64_t parallelFactor = 0; | |
294 | uint64_t blockSize = 0; | |
295 | parseHashed(hash, salt, hashedPassword, workFactor, parallelFactor, blockSize); | |
296 | ||
297 | auto expected = hashPasswordInternal(password, salt, workFactor, parallelFactor, blockSize); | |
298 | ||
299 | return constantTimeStringEquals(expected, hashedPassword); | |
cfe95ada | 300 | #else |
8a6030b6 | 301 | throw std::runtime_error("Verifying a hashed password requires scrypt support in OpenSSL, and it is not available"); |
cfe95ada RG |
302 | #endif |
303 | } | |
304 | ||
305 | bool isPasswordHashed(const std::string& password) | |
306 | { | |
2f32819a | 307 | #if !defined(DISABLE_HASHED_CREDENTIALS) && defined(HAVE_EVP_PKEY_CTX_SET1_SCRYPT_SALT) |
8a6030b6 RG |
308 | if (password.size() < pwhash_prefix_size || password.size() > pwhash_max_size) { |
309 | return false; | |
310 | } | |
311 | ||
312 | if (!boost::starts_with(password, pwhash_prefix)) { | |
cfe95ada RG |
313 | return false; |
314 | } | |
315 | ||
8a6030b6 RG |
316 | auto parametersEnd = password.find('$', pwhash_prefix.size()); |
317 | if (parametersEnd == std::string::npos || parametersEnd == password.size()) { | |
318 | return false; | |
319 | } | |
cfe95ada | 320 | |
4ec3ff03 RG |
321 | size_t parametersSize = parametersEnd - pwhash_prefix.size(); |
322 | /* ln=X,p=Y,r=Z */ | |
323 | if (parametersSize < 12) { | |
324 | return false; | |
325 | } | |
326 | ||
8a6030b6 RG |
327 | auto saltEnd = password.find('$', parametersEnd + 1); |
328 | if (saltEnd == std::string::npos || saltEnd == password.size()) { | |
cfe95ada RG |
329 | return false; |
330 | } | |
8a6030b6 RG |
331 | |
332 | /* the salt is base64 encoded so it has to be larger than that */ | |
333 | if ((saltEnd - parametersEnd - 1) < pwhash_salt_size) { | |
334 | return false; | |
335 | } | |
336 | ||
337 | /* the hash base64 encoded so it has to be larger than that */ | |
31ec1c9f | 338 | if ((password.size() - saltEnd - 1) < pwhash_output_size) { |
8a6030b6 RG |
339 | return false; |
340 | } | |
341 | ||
cfe95ada RG |
342 | return true; |
343 | #else | |
344 | return false; | |
345 | #endif | |
346 | } | |
347 | ||
348 | /* if the password is in cleartext and hashing is available, | |
349 | the hashed form will be kept in memory */ | |
31ec1c9f RG |
350 | CredentialsHolder::CredentialsHolder(std::string&& password, bool hashPlaintext) : |
351 | d_credentials(std::move(password)) | |
cfe95ada | 352 | { |
cfe95ada | 353 | if (isHashingAvailable()) { |
8a6030b6 | 354 | if (!isPasswordHashed(d_credentials.getString())) { |
64c4f83c | 355 | if (hashPlaintext) { |
8a6030b6 RG |
356 | d_salt = generateRandomSalt(); |
357 | d_workFactor = s_defaultWorkFactor; | |
358 | d_parallelFactor = s_defaultParallelFactor; | |
359 | d_blockSize = s_defaultBlockSize; | |
360 | d_credentials = SensitiveData(hashPasswordInternal(d_credentials.getString(), d_salt, d_workFactor, d_parallelFactor, d_blockSize)); | |
64c4f83c RG |
361 | d_isHashed = true; |
362 | } | |
cfe95ada RG |
363 | } |
364 | else { | |
32e8669a | 365 | d_wasHashed = true; |
64c4f83c | 366 | d_isHashed = true; |
8a6030b6 RG |
367 | std::string hashedPassword; |
368 | parseHashed(d_credentials.getString(), d_salt, hashedPassword, d_workFactor, d_parallelFactor, d_blockSize); | |
369 | d_credentials = SensitiveData(std::move(hashedPassword)); | |
cfe95ada RG |
370 | } |
371 | } | |
64c4f83c RG |
372 | |
373 | if (!d_isHashed) { | |
b2504b29 | 374 | d_fallbackHashPerturb = random(); |
8a6030b6 | 375 | d_fallbackHash = burtle(reinterpret_cast<const unsigned char*>(d_credentials.getString().data()), d_credentials.getString().size(), d_fallbackHashPerturb); |
cfe95ada | 376 | } |
cfe95ada RG |
377 | } |
378 | ||
379 | CredentialsHolder::~CredentialsHolder() | |
380 | { | |
b2504b29 RG |
381 | d_fallbackHashPerturb = 0; |
382 | d_fallbackHash = 0; | |
cfe95ada RG |
383 | } |
384 | ||
385 | bool CredentialsHolder::matches(const std::string& password) const | |
386 | { | |
64c4f83c | 387 | if (d_isHashed) { |
8a6030b6 | 388 | return verifyPassword(d_credentials.getString(), d_salt, d_workFactor, d_parallelFactor, d_blockSize, password); |
cfe95ada RG |
389 | } |
390 | else { | |
b2504b29 RG |
391 | uint32_t fallback = burtle(reinterpret_cast<const unsigned char*>(password.data()), password.size(), d_fallbackHashPerturb); |
392 | if (fallback != d_fallbackHash) { | |
393 | return false; | |
394 | } | |
395 | ||
8a6030b6 | 396 | return constantTimeStringEquals(password, d_credentials.getString()); |
cfe95ada RG |
397 | } |
398 | } | |
399 | ||
400 | bool CredentialsHolder::isHashingAvailable() | |
401 | { | |
2f32819a | 402 | #if !defined(DISABLE_HASHED_CREDENTIALS) && defined(HAVE_EVP_PKEY_CTX_SET1_SCRYPT_SALT) |
cfe95ada RG |
403 | return true; |
404 | #else | |
405 | return false; | |
406 | #endif | |
407 | } | |
061bc5f0 | 408 | |
dc6aa7f5 | 409 | #include <csignal> |
061bc5f0 RG |
410 | #include <termios.h> |
411 | ||
8a6030b6 | 412 | SensitiveData CredentialsHolder::readFromTerminal() |
061bc5f0 RG |
413 | { |
414 | struct termios term; | |
415 | struct termios oterm; | |
061bc5f0 RG |
416 | bool restoreTermSettings = false; |
417 | int termAction = TCSAFLUSH; | |
418 | #ifdef TCSASOFT | |
9437be08 | 419 | termAction |= TCSASOFT; |
061bc5f0 RG |
420 | #endif |
421 | ||
422 | FDWrapper input(open("/dev/tty", O_RDONLY)); | |
423 | if (int(input) != -1) { | |
424 | if (tcgetattr(input, &oterm) == 0) { | |
425 | memcpy(&term, &oterm, sizeof(term)); | |
426 | term.c_lflag &= ~(ECHO | ECHONL); | |
427 | tcsetattr(input, termAction, &term); | |
428 | restoreTermSettings = true; | |
429 | } | |
430 | } | |
431 | else { | |
432 | input = FDWrapper(dup(STDIN_FILENO)); | |
d78e47e3 | 433 | restoreTermSettings = false; |
061bc5f0 | 434 | } |
d78e47e3 | 435 | |
061bc5f0 RG |
436 | FDWrapper output(open("/dev/tty", O_WRONLY)); |
437 | if (int(output) == -1) { | |
438 | output = FDWrapper(dup(STDERR_FILENO)); | |
439 | } | |
440 | ||
441 | struct std::map<int, struct sigaction> signals; | |
442 | struct sigaction sa; | |
443 | sigemptyset(&sa.sa_mask); | |
444 | sa.sa_flags = 0; | |
31ec1c9f | 445 | sa.sa_handler = [](int s) {}; |
061bc5f0 RG |
446 | sigaction(SIGALRM, &sa, &signals[SIGALRM]); |
447 | sigaction(SIGHUP, &sa, &signals[SIGHUP]); | |
448 | sigaction(SIGINT, &sa, &signals[SIGINT]); | |
449 | sigaction(SIGPIPE, &sa, &signals[SIGPIPE]); | |
450 | sigaction(SIGQUIT, &sa, &signals[SIGQUIT]); | |
451 | sigaction(SIGTERM, &sa, &signals[SIGTERM]); | |
452 | sigaction(SIGTSTP, &sa, &signals[SIGTSTP]); | |
453 | sigaction(SIGTTIN, &sa, &signals[SIGTTIN]); | |
454 | sigaction(SIGTTOU, &sa, &signals[SIGTTOU]); | |
455 | ||
456 | std::string buffer; | |
457 | /* let's allocate a huge buffer now to prevent reallocation, | |
458 | which would leave parts of the buffer around */ | |
459 | buffer.reserve(512); | |
460 | ||
461 | for (;;) { | |
462 | char ch = '\0'; | |
463 | auto got = read(input, &ch, 1); | |
464 | if (got == 1 && ch != '\n' && ch != '\r') { | |
465 | buffer.push_back(ch); | |
466 | } | |
467 | else { | |
468 | break; | |
469 | } | |
470 | } | |
471 | ||
061bc5f0 RG |
472 | if (restoreTermSettings) { |
473 | tcsetattr(input, termAction, &oterm); | |
474 | } | |
475 | ||
476 | for (const auto& sig : signals) { | |
477 | sigaction(sig.first, &sig.second, nullptr); | |
478 | } | |
479 | ||
8a6030b6 | 480 | return SensitiveData(std::move(buffer)); |
061bc5f0 | 481 | } |