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)
23 * @brief Implementation of PasswordFile, used to manage password files.
25 #include <password-file.h>
30 #include <openssl/sha.h>
34 #include <dpl/log/log.h>
36 #include <security-server.h>
37 #include <protocols.h>
38 #include <password-exception.h>
39 #include <password-file-buffer.h>
44 const std::string DATA_DIR = "/opt/data/security-server";
45 const std::string PASSWORD_FILE = "password.pwd";
46 const std::string ATTEMPT_FILE = "attempt";
47 const double RETRY_TIMEOUT = 0.5;
49 namespace SecurityServer
51 PasswordFile::Password::Password()
53 m_password = PasswordFile::hashPassword("");
56 PasswordFile::Password::Password(const RawHash& password)
58 m_password = password;
61 PasswordFile::Password::Password(IStream& stream)
63 Deserialization::Deserialize(stream, m_password);
66 void PasswordFile::Password::Serialize(IStream &stream) const
68 Serialization::Serialize(stream, m_password);
71 PasswordFile::PasswordFile(): m_maxAttempt(PASSWORD_INFINITE_ATTEMPT_COUNT), m_historySize(0),
72 m_expireTime(PASSWORD_INFINITE_EXPIRATION_TIME), m_attempt(0)
74 // check if data directory exists
76 if (!dirExists(DATA_DIR.c_str())) {
77 if(mkdir(DATA_DIR.c_str(), 0700)) {
78 LogError("Failed to create directory for files. Error: " << strerror(errno));
79 Throw(PasswordException::MakeDirError);
88 void PasswordFile::resetTimer()
90 m_retryTimerStart = std::chrono::monotonic_clock::now();
91 m_retryTimerStart -= TimeDiff(RETRY_TIMEOUT);
94 void PasswordFile::preparePwdFile()
96 std::string s_pwdfilePath = DATA_DIR + "/" + PASSWORD_FILE;
98 // check if password file exists
100 if (!fileExists(s_pwdfilePath)) {
101 LogSecureDebug("PWD_DBG not found password file. Creating.");
102 __mode_t oldMask = umask(S_IRUSR | S_IWUSR);
108 } else { //if file exists, load data
109 LogSecureDebug("PWD_DBG found password file. Opening.");
110 loadMemoryFromFile();
114 void PasswordFile::prepareAttemptFile()
116 std::string s_attemptfilePath = DATA_DIR + "/" + ATTEMPT_FILE;
118 // check if attempt file exists
120 if (!fileExists(s_attemptfilePath)) {
121 LogSecureDebug("PWD_DBG not found attempt file. Creating.");
122 __mode_t oldMask = umask(S_IRUSR | S_IWUSR);
124 writeAttemptToFile();
128 LogSecureDebug("PWD_DBG found attempt file. Opening.");
129 std::ifstream attemptFile(s_attemptfilePath);
130 if(!attemptFile.good()) {
131 LogError("Failed to open attempt file.");
132 Throw(PasswordException::FStreamOpenError);
135 attemptFile.read(reinterpret_cast<char*>(&m_attempt), sizeof(unsigned int));
137 LogError("Failed to read attempt count.");
138 Throw(PasswordException::FStreamReadError);
143 bool PasswordFile::fileExists(const std::string &filename) const
147 return ((stat(filename.c_str(), &buf) == 0));
150 bool PasswordFile::dirExists(const std::string &dirpath) const
154 return ((stat(dirpath.c_str(), &buf) == 0) && (((buf.st_mode) & S_IFMT) == S_IFDIR));
157 void PasswordFile::writeMemoryToFile() const
159 PasswordFileBuffer pwdBuffer;
161 //serialize password attributes
162 Serialization::Serialize(pwdBuffer, m_maxAttempt);
163 Serialization::Serialize(pwdBuffer, m_historySize);
164 Serialization::Serialize(pwdBuffer, m_expireTime);
165 Serialization::Serialize(pwdBuffer, m_passwords);
167 pwdBuffer.Save(DATA_DIR + "/" + PASSWORD_FILE);
170 void PasswordFile::loadMemoryFromFile()
172 PasswordFileBuffer pwdFile;
174 pwdFile.Load(DATA_DIR + "/" + PASSWORD_FILE);
178 Deserialization::Deserialize(pwdFile, m_maxAttempt);
179 Deserialization::Deserialize(pwdFile, m_historySize);
180 Deserialization::Deserialize(pwdFile, m_expireTime);
181 Deserialization::Deserialize(pwdFile, m_passwords);
184 void PasswordFile::writeAttemptToFile() const
186 std::ofstream attemptFile(DATA_DIR + "/" + ATTEMPT_FILE, std::ofstream::trunc);
188 if(!attemptFile.good()) {
189 LogError("Failed to open attempt file.");
190 Throw(PasswordException::FStreamOpenError);
193 attemptFile.write(reinterpret_cast<const char*>(&m_attempt), sizeof(unsigned int));
195 LogError("Failed to write attempt count.");
196 Throw(PasswordException::FStreamWriteError);
201 if (0 <= (fd = open((DATA_DIR + "/" + ATTEMPT_FILE).c_str(), O_WRONLY | O_APPEND))) {
202 fsync(fd); // force synchronization system buffers with file
206 LogError("Failed to sync attempt file: " << DATA_DIR << "/" << ATTEMPT_FILE << "strerror: " << strerror(err));
210 bool PasswordFile::isPasswordActive() const
212 return !(m_passwords.empty());
215 void PasswordFile::setHistory(unsigned int history)
217 //setting history should be independent from password being set
218 m_historySize = history;
220 //we want to keep 1 current pwd, plus history amount of passwords.
221 if(m_passwords.size() > 1+history)
222 m_passwords.resize(1+history);
225 unsigned int PasswordFile::getHistorySize() const
227 return m_historySize;
230 unsigned int PasswordFile::getAttempt() const
235 void PasswordFile::resetAttempt()
240 void PasswordFile::incrementAttempt()
245 int PasswordFile::getMaxAttempt() const
250 void PasswordFile::setMaxAttempt(unsigned int maxAttempt)
252 m_maxAttempt = maxAttempt;
255 bool PasswordFile::isPasswordReused(const std::string &password) const
257 RawHash hashedPwd = hashPassword(password);
259 LogSecureDebug("PwdCount: " << m_passwords.size() << ", PwdMaxHistory: " << getHistorySize());
261 if(std::find_if(m_passwords.begin(), m_passwords.end(),
262 [&hashedPwd](const Password& pwd) { return (pwd.m_password == hashedPwd); })
263 != m_passwords.end()) {
264 LogSecureDebug("Passwords match!");
268 LogSecureDebug("isPasswordReused: No passwords match, password not reused.");
272 void PasswordFile::setPassword(const std::string &password)
274 RawHash hashedPwd = hashPassword(password);
276 m_passwords.push_front(Password(hashedPwd));
278 //one current password, plus history amount of passwords
279 if(m_passwords.size() > 1+getHistorySize())
280 m_passwords.pop_back();
283 bool PasswordFile::checkPassword(const std::string &password) const
285 RawHash hashedPwd = hashPassword(password);
287 return (hashedPwd == m_passwords.begin()->m_password);
290 void PasswordFile::setExpireTime(int expireTime)
292 if(isPasswordActive())
293 m_expireTime = expireTime;
295 LogError("Can't set expiration time, password not active.");
296 Throw(PasswordException::PasswordNotActive);
300 time_t PasswordFile::getExpireTime() const
305 time_t PasswordFile::getExpireTimeLeft() const
307 if(m_expireTime != PASSWORD_INFINITE_EXPIRATION_TIME)
308 return (m_expireTime - time(NULL));
313 bool PasswordFile::checkExpiration() const
315 //return true if expired, else false
316 return ((m_expireTime != PASSWORD_INFINITE_EXPIRATION_TIME) && (time(NULL) > m_expireTime));
319 bool PasswordFile::checkIfAttemptsExceeded() const
321 return ((m_maxAttempt != PASSWORD_INFINITE_ATTEMPT_COUNT) && (m_attempt >= m_maxAttempt));
324 bool PasswordFile::isIgnorePeriod() const
326 TimePoint retryTimerStop = std::chrono::monotonic_clock::now();
327 TimeDiff diff = retryTimerStop - m_retryTimerStart;
329 m_retryTimerStart = retryTimerStop;
331 return (diff.count() < RETRY_TIMEOUT);
334 //hashPassword is also used in Password struct constructor, that's why it's static. Moreover
335 //it is assumed that incorrect input password was checked earlier.
336 PasswordFile::RawHash PasswordFile::hashPassword(const std::string &password)
338 RawHash result(SHA256_DIGEST_LENGTH);
341 SHA256_Init(&context);
342 SHA256_Update(&context, reinterpret_cast<const unsigned char*>(password.c_str()),
344 SHA256_Final(result.data(), &context);
348 } //namespace SecurityServer