2 * Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd All Rights Reserved
4 * Contact: Bumjin Im <bj.im@samsung.com>
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License
19 * @file password-file.cpp
20 * @author Zbigniew Jasinski (z.jasinski@samsung.com)
21 * @author Lukasz Kostyra (l.kostyra@partner.samsung.com)
22 * @author Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
24 * @brief Implementation of PasswordFile, used to manage password files.
26 #include <password-file.h>
37 #include <openssl/sha.h>
39 #include <dpl/log/log.h>
40 #include <dpl/fstream_accessors.h>
42 #include <security-server.h>
43 #include <protocols.h>
44 #include <password-exception.h>
45 #include <password-file-buffer.h>
48 const std::string DATA_DIR = "/opt/data/security-server";
49 const std::string PASSWORD_FILE = DATA_DIR + "/password";
50 const std::string OLD_VERSION_PASSWORD_FILE = DATA_DIR + "/password.pwd";
51 const std::string ATTEMPT_FILE = DATA_DIR + "/attempt";
52 const double RETRY_TIMEOUT = 0.5;
53 const mode_t FILE_MODE = S_IRUSR | S_IWUSR;
54 const unsigned int CURRENT_FILE_VERSION = 3;
55 } // namespace anonymous
57 namespace SecurityServer
59 const time_t PASSWORD_INFINITE_EXPIRATION_TIME = std::numeric_limits<time_t>::max();
61 class NoPassword: public IPassword
64 NoPassword(IStream&) {}
67 void Serialize(IStream &stream) const
69 Serialization::Serialize(stream, static_cast<unsigned int>(PasswordType::NONE));
72 bool match(const std::string &) const
78 class SHA256Password: public IPassword
81 SHA256Password(IStream& stream)
83 Deserialization::Deserialize(stream, m_hash);
86 SHA256Password(const std::string &password)
87 : m_hash(hash(password)) {}
89 SHA256Password(const RawHash& hash)
92 void Serialize(IStream &stream) const
94 Serialization::Serialize(stream, static_cast<unsigned int>(PasswordType::SHA256));
95 Serialization::Serialize(stream, m_hash);
98 bool match(const std::string &password) const
100 return m_hash == hash(password);
105 static RawHash hash(const std::string &password)
107 RawHash result(SHA256_DIGEST_LENGTH);
110 SHA256_Init(&context);
111 SHA256_Update(&context, reinterpret_cast<const unsigned char*>(password.c_str()),
113 SHA256_Final(result.data(), &context);
119 // deserialization of new password format
121 void Deserialization::Deserialize(IStream& stream, IPasswordPtr& ptr)
123 unsigned int algorithm;
124 Deserialization::Deserialize(stream, algorithm);
126 case IPassword::PasswordType::NONE:
127 ptr.reset(new NoPassword());
129 case IPassword::PasswordType::SHA256:
130 ptr.reset(new SHA256Password(stream));
133 Throw(PasswordException::FStreamReadError);
137 PasswordFile::PasswordFile(): m_passwordCurrent(new NoPassword()),
138 m_maxAttempt(PASSWORD_INFINITE_ATTEMPT_COUNT),
140 m_expireTime(PASSWORD_INFINITE_EXPIRATION_TIME),
141 m_passwordActive(false), m_attempt(0)
143 // check if data directory exists
145 if (!dirExists(DATA_DIR.c_str())) {
146 if(mkdir(DATA_DIR.c_str(), 0700)) {
147 LogError("Failed to create directory for files. Error: " << strerror(errno));
148 Throw(PasswordException::MakeDirError);
153 prepareAttemptFile();
157 void PasswordFile::resetState()
159 m_passwordCurrent.reset(new NoPassword());
160 m_maxAttempt = PASSWORD_INFINITE_ATTEMPT_COUNT;
161 m_maxHistorySize = 0;
162 m_expireTime = PASSWORD_INFINITE_EXPIRATION_TIME;
163 m_passwordActive = false;
166 void PasswordFile::resetTimer()
168 m_retryTimerStart = ClockType::now();
169 m_retryTimerStart -= TimeDiff(RETRY_TIMEOUT);
172 void PasswordFile::preparePwdFile()
174 // check if password file exists
175 if (!fileExists(PASSWORD_FILE)) {
176 // if old format file exist - load it
177 if (tryLoadMemoryFromOldFormatFile()) {
178 // save in new format
180 // and remove old file
181 remove(OLD_VERSION_PASSWORD_FILE.c_str());
185 LogSecureDebug("PWD_DBG not found password file. Creating.");
189 } else { //if file exists, load data
190 LogSecureDebug("PWD_DBG found password file. Opening.");
192 loadMemoryFromFile();
194 LogError("Invalid " << PASSWORD_FILE << " file format");
201 void PasswordFile::prepareAttemptFile()
203 // check if attempt file exists
205 if (!fileExists(ATTEMPT_FILE)) {
206 LogSecureDebug("PWD_DBG not found attempt file. Creating.");
208 writeAttemptToFile();
210 LogSecureDebug("PWD_DBG found attempt file. Opening.");
211 std::ifstream attemptFile(ATTEMPT_FILE);
213 LogError("Failed to open attempt file.");
218 attemptFile.read(reinterpret_cast<char*>(&m_attempt), sizeof(unsigned int));
220 LogError("Failed to read attempt count.");
227 bool PasswordFile::fileExists(const std::string &filename) const
231 return ((stat(filename.c_str(), &buf) == 0));
234 bool PasswordFile::dirExists(const std::string &dirpath) const
238 return ((stat(dirpath.c_str(), &buf) == 0) && (((buf.st_mode) & S_IFMT) == S_IFDIR));
241 void PasswordFile::writeMemoryToFile() const
243 PasswordFileBuffer pwdBuffer;
245 LogSecureDebug("Saving max_att: " << m_maxAttempt << ", history_size: " <<
246 m_maxHistorySize << ", m_expireTime: " << m_expireTime << ", isActive: " <<
249 //serialize password attributes
250 Serialization::Serialize(pwdBuffer, CURRENT_FILE_VERSION);
251 Serialization::Serialize(pwdBuffer, m_maxAttempt);
252 Serialization::Serialize(pwdBuffer, m_maxHistorySize);
253 Serialization::Serialize(pwdBuffer, m_expireTime);
254 Serialization::Serialize(pwdBuffer, m_passwordActive);
255 Serialization::Serialize(pwdBuffer, m_passwordCurrent);
256 Serialization::Serialize(pwdBuffer, m_passwordHistory);
258 pwdBuffer.Save(PASSWORD_FILE);
260 chmod(PASSWORD_FILE.c_str(), FILE_MODE);
263 void PasswordFile::loadMemoryFromFile()
265 PasswordFileBuffer pwdFile;
267 pwdFile.Load(PASSWORD_FILE);
269 unsigned int fileVersion = 0;
270 Deserialization::Deserialize(pwdFile, fileVersion);
271 if (fileVersion != CURRENT_FILE_VERSION)
272 Throw(PasswordException::FStreamReadError);
274 m_passwordHistory.clear();
276 Deserialization::Deserialize(pwdFile, m_maxAttempt);
277 Deserialization::Deserialize(pwdFile, m_maxHistorySize);
278 Deserialization::Deserialize(pwdFile, m_expireTime);
279 Deserialization::Deserialize(pwdFile, m_passwordActive);
280 Deserialization::Deserialize(pwdFile, m_passwordCurrent);
281 Deserialization::Deserialize(pwdFile, m_passwordHistory);
283 LogSecureDebug("Loaded max_att: " << m_maxAttempt << ", history_size: " <<
284 m_maxHistorySize << ", m_expireTime: " << m_expireTime << ", isActive: " <<
288 bool PasswordFile::tryLoadMemoryFromOldFormatFile()
290 struct stat oldFileStat;
291 if (stat(OLD_VERSION_PASSWORD_FILE.c_str(), &oldFileStat) != 0)
294 static const int ELEMENT_SIZE = sizeof(unsigned) + SHA256_DIGEST_LENGTH;
295 static const int VERSION_1_REMAINING = sizeof(unsigned) * 4;
296 static const int VERSION_2_REMAINING = VERSION_1_REMAINING + sizeof(bool);
297 int remaining = oldFileStat.st_size % ELEMENT_SIZE;
299 if (remaining != VERSION_1_REMAINING && remaining != VERSION_2_REMAINING)
303 PasswordFileBuffer pwdFile;
304 pwdFile.Load(OLD_VERSION_PASSWORD_FILE);
306 Deserialization::Deserialize(pwdFile, m_maxAttempt);
307 Deserialization::Deserialize(pwdFile, m_maxHistorySize);
308 Deserialization::Deserialize(pwdFile, m_expireTime);
309 if (m_expireTime == 0)
310 m_expireTime = PASSWORD_INFINITE_EXPIRATION_TIME;
311 if (remaining == VERSION_2_REMAINING)
312 Deserialization::Deserialize(pwdFile, m_passwordActive);
314 m_passwordActive = true;
316 // deserialize passwords in old format
319 OldPassword(IStream &stream)
321 Deserialization::Deserialize(stream, m_hash);
323 IPassword::RawHash m_hash;
325 std::list<OldPassword> oldFormatPasswords;
326 Deserialization::Deserialize(pwdFile, oldFormatPasswords);
328 // convert passwords to new format
329 m_passwordHistory.clear();
330 if (oldFormatPasswords.empty()) {
331 m_passwordCurrent.reset(new NoPassword());
332 m_passwordActive = false;
334 m_passwordCurrent.reset(new SHA256Password(oldFormatPasswords.front().m_hash));
335 std::for_each(++oldFormatPasswords.begin(), oldFormatPasswords.end(),
336 [&] (const OldPassword& pwd)
337 {m_passwordHistory.push_back(IPasswordPtr(new SHA256Password(pwd.m_hash)));}
341 LogWarning("Invalid " << OLD_VERSION_PASSWORD_FILE << " file format");
349 void PasswordFile::writeAttemptToFile() const
351 std::ofstream attemptFile(ATTEMPT_FILE, std::ofstream::trunc);
353 if(!attemptFile.good()) {
354 LogError("Failed to open attempt file.");
355 Throw(PasswordException::FStreamOpenError);
358 attemptFile.write(reinterpret_cast<const char*>(&m_attempt), sizeof(unsigned int));
360 LogError("Failed to write attempt count.");
361 Throw(PasswordException::FStreamWriteError);
365 fsync(DPL::FstreamAccessors<std::ofstream>::GetFd(attemptFile)); // flush kernel space buffer
369 void PasswordFile::activatePassword()
371 m_passwordActive = true;
374 bool PasswordFile::isPasswordActive() const
376 return m_passwordActive;
379 void PasswordFile::setMaxHistorySize(unsigned int history)
381 //setting history should be independent from password being set
382 m_maxHistorySize = history;
384 while(m_passwordHistory.size() > history)
385 m_passwordHistory.pop_back();
388 unsigned int PasswordFile::getMaxHistorySize() const
390 return m_maxHistorySize;
393 unsigned int PasswordFile::getAttempt() const
398 void PasswordFile::resetAttempt()
403 void PasswordFile::incrementAttempt()
408 int PasswordFile::getMaxAttempt() const
413 void PasswordFile::setMaxAttempt(unsigned int maxAttempt)
415 m_maxAttempt = maxAttempt;
418 bool PasswordFile::isPasswordReused(const std::string &password) const
420 LogSecureDebug("Checking if pwd is reused. HistorySize: " << m_passwordHistory.size() <<
421 ", MaxHistorySize: " << getMaxHistorySize());
423 //go through history and check if password existed earlier
424 if(std::any_of(m_passwordHistory.begin(), m_passwordHistory.end(),
425 [&password](const IPasswordPtr& pwd) { return pwd->match(password); })) {
426 LogSecureDebug("Passwords match!");
430 LogSecureDebug("isPasswordReused: No passwords match, password not reused.");
434 void PasswordFile::setPassword(const std::string &password)
436 //put current password to history
437 m_passwordHistory.push_front(std::move(m_passwordCurrent));
439 //erase last password if we exceed max history size
440 if(m_passwordHistory.size() > getMaxHistorySize())
441 m_passwordHistory.pop_back();
443 //replace current password with new one
444 m_passwordCurrent.reset(new SHA256Password(password));
447 bool PasswordFile::checkPassword(const std::string &password) const
449 return m_passwordCurrent->match(password);
452 void PasswordFile::setExpireTime(time_t expireTime)
454 if(isPasswordActive())
455 m_expireTime = expireTime;
457 LogError("Can't set expiration time, password not active.");
458 Throw(PasswordException::PasswordNotActive);
462 unsigned int PasswordFile::getExpireTimeLeft() const
464 if(m_expireTime != PASSWORD_INFINITE_EXPIRATION_TIME) {
465 time_t timeLeft = m_expireTime - time(NULL);
466 return (timeLeft < 0) ? 0 : static_cast<unsigned int>(timeLeft);
468 return PASSWORD_API_NO_EXPIRATION;
471 bool PasswordFile::checkExpiration() const
473 //return true if expired, else false
474 return ((m_expireTime != PASSWORD_INFINITE_EXPIRATION_TIME) && (time(NULL) > m_expireTime));
477 bool PasswordFile::checkIfAttemptsExceeded() const
479 return ((m_maxAttempt != PASSWORD_INFINITE_ATTEMPT_COUNT) && (m_attempt > m_maxAttempt));
482 bool PasswordFile::isIgnorePeriod() const
484 TimePoint retryTimerStop = ClockType::now();
485 TimeDiff diff = retryTimerStop - m_retryTimerStart;
487 m_retryTimerStart = retryTimerStop;
489 return (diff.count() < RETRY_TIMEOUT);
492 bool PasswordFile::isHistoryActive() const
494 return (m_maxHistorySize != 0);
496 } //namespace SecurityServer