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 <password-exception.h>
38 #include <password-file-buffer.h>
43 const std::string DATA_DIR = "/opt/data/security-server";
44 const std::string PASSWORD_FILE = "password.pwd";
45 const std::string ATTEMPT_FILE = "attempt";
46 const double RETRY_TIMEOUT = 0.5;
48 namespace SecurityServer
50 PasswordFile::Password::Password()
52 m_password = PasswordFile::hashPassword("");
55 PasswordFile::Password::Password(const RawHash& password)
57 m_password = password;
60 PasswordFile::Password::Password(IStream& stream)
62 Deserialization::Deserialize(stream, m_password);
65 void PasswordFile::Password::Serialize(IStream &stream) const
67 Serialization::Serialize(stream, m_password);
70 PasswordFile::PasswordFile(): m_maxAttempt(0), m_historySize(0), m_expireTime(0), m_attempt(0)
72 // check if data directory exists
74 if (!dirExists(DATA_DIR.c_str())) {
75 if(mkdir(DATA_DIR.c_str(), 0700)) {
76 LogError("Failed to create directory for files. Error: " << strerror(errno));
77 Throw(PasswordException::MakeDirError);
86 void PasswordFile::resetTimer()
88 m_retryTimerStart = std::chrono::monotonic_clock::now();
89 m_retryTimerStart -= TimeDiff(RETRY_TIMEOUT);
92 void PasswordFile::preparePwdFile()
94 std::string s_pwdfilePath = DATA_DIR + "/" + PASSWORD_FILE;
96 // check if password file exists
98 if (!fileExists(s_pwdfilePath)) {
99 LogSecureDebug("PWD_DBG not found password file. Creating.");
100 __mode_t oldMask = umask(S_IRUSR | S_IWUSR);
106 } else { //if file exists, load data
107 LogSecureDebug("PWD_DBG found password file. Opening.");
108 loadMemoryFromFile();
112 void PasswordFile::prepareAttemptFile()
114 std::string s_attemptfilePath = DATA_DIR + "/" + ATTEMPT_FILE;
116 // check if attempt file exists
118 if (!fileExists(s_attemptfilePath)) {
119 LogSecureDebug("PWD_DBG not found attempt file. Creating.");
120 __mode_t oldMask = umask(S_IRUSR | S_IWUSR);
122 writeAttemptToFile();
126 LogSecureDebug("PWD_DBG found attempt file. Opening.");
127 std::ifstream attemptFile(s_attemptfilePath);
128 if(!attemptFile.good()) {
129 LogError("Failed to open attempt file.");
130 Throw(PasswordException::FStreamOpenError);
133 attemptFile.read(reinterpret_cast<char*>(&m_attempt), sizeof(unsigned int));
135 LogError("Failed to read attempt count.");
136 Throw(PasswordException::FStreamReadError);
141 bool PasswordFile::fileExists(const std::string &filename) const
145 return ((stat(filename.c_str(), &buf) == 0));
148 bool PasswordFile::dirExists(const std::string &dirpath) const
152 return ((stat(dirpath.c_str(), &buf) == 0) && (((buf.st_mode) & S_IFMT) == S_IFDIR));
155 void PasswordFile::writeMemoryToFile() const
157 PasswordFileBuffer pwdBuffer;
159 //serialize password attributes
160 Serialization::Serialize(pwdBuffer, m_maxAttempt);
161 Serialization::Serialize(pwdBuffer, m_historySize);
162 Serialization::Serialize(pwdBuffer, m_expireTime);
163 Serialization::Serialize(pwdBuffer, m_passwords);
165 pwdBuffer.Save(DATA_DIR + "/" + PASSWORD_FILE);
168 void PasswordFile::loadMemoryFromFile()
170 PasswordFileBuffer pwdFile;
172 pwdFile.Load(DATA_DIR + "/" + PASSWORD_FILE);
176 Deserialization::Deserialize(pwdFile, m_maxAttempt);
177 Deserialization::Deserialize(pwdFile, m_historySize);
178 Deserialization::Deserialize(pwdFile, m_expireTime);
179 Deserialization::Deserialize(pwdFile, m_passwords);
182 void PasswordFile::writeAttemptToFile() const
184 std::ofstream attemptFile(DATA_DIR + "/" + ATTEMPT_FILE, std::ofstream::trunc);
186 if(!attemptFile.good()) {
187 LogError("Failed to open attempt file.");
188 Throw(PasswordException::FStreamOpenError);
191 attemptFile.write(reinterpret_cast<const char*>(&m_attempt), sizeof(unsigned int));
193 LogError("Failed to write attempt count.");
194 Throw(PasswordException::FStreamWriteError);
199 if (0 <= (fd = open((DATA_DIR + "/" + ATTEMPT_FILE).c_str(), O_WRONLY | O_APPEND))) {
200 fsync(fd); // force synchronization system buffers with file
204 LogError("Failed to sync attempt file: " << DATA_DIR << "/" << ATTEMPT_FILE << "strerror: " << strerror(err));
208 bool PasswordFile::isPasswordActive() const
210 return !(m_passwords.empty());
213 void PasswordFile::setHistory(unsigned int history)
215 //setting history should be independent from password being set
216 m_historySize = history;
218 //we want to keep 1 current pwd, plus history amount of passwords.
219 if(m_passwords.size() > 1+history)
220 m_passwords.resize(1+history);
223 unsigned int PasswordFile::getHistorySize() const
225 return m_historySize;
228 unsigned int PasswordFile::getAttempt() const
233 void PasswordFile::resetAttempt()
238 void PasswordFile::incrementAttempt()
243 int PasswordFile::getMaxAttempt() const
248 void PasswordFile::setMaxAttempt(unsigned int maxAttempt)
250 m_maxAttempt = maxAttempt;
253 bool PasswordFile::isPasswordReused(const std::string &password) const
255 RawHash hashedPwd = hashPassword(password);
257 LogSecureDebug("PwdCount: " << m_passwords.size() << ", PwdMaxHistory: " << getHistorySize());
259 if(std::find_if(m_passwords.begin(), m_passwords.end(),
260 [&hashedPwd](const Password& pwd) { return (pwd.m_password == hashedPwd); })
261 != m_passwords.end()) {
262 LogSecureDebug("Passwords match!");
266 LogSecureDebug("isPasswordReused: No passwords match, password not reused.");
270 void PasswordFile::setPassword(const std::string &password)
272 RawHash hashedPwd = hashPassword(password);
274 m_passwords.push_front(Password(hashedPwd));
276 //one current password, plus history amount of passwords
277 if(m_passwords.size() > 1+getHistorySize())
278 m_passwords.pop_back();
281 bool PasswordFile::checkPassword(const std::string &password) const
283 RawHash hashedPwd = hashPassword(password);
285 return (hashedPwd == m_passwords.begin()->m_password);
288 void PasswordFile::setExpireTime(int expireTime)
290 if(isPasswordActive())
291 m_expireTime = expireTime;
293 LogError("Can't set expiration time, password not active.");
294 Throw(PasswordException::PasswordNotActive);
298 time_t PasswordFile::getExpireTime() const
303 time_t PasswordFile::getExpireTimeLeft() const
306 return (m_expireTime - time(NULL));
311 bool PasswordFile::checkExpiration() const
313 //return true if expired, else false
314 return ((m_expireTime != 0) && (time(NULL) > m_expireTime));
317 bool PasswordFile::isIgnorePeriod() const
319 TimePoint retryTimerStop = std::chrono::monotonic_clock::now();
320 TimeDiff diff = retryTimerStop - m_retryTimerStart;
322 m_retryTimerStart = retryTimerStop;
324 return (diff.count() < RETRY_TIMEOUT);
327 //hashPassword is also used in Password struct constructor, that's why it's static. Moreover
328 //it is assumed that incorrect input password was checked earlier.
329 PasswordFile::RawHash PasswordFile::hashPassword(const std::string &password)
331 RawHash result(SHA256_DIGEST_LENGTH);
334 SHA256_Init(&context);
335 SHA256_Update(&context, reinterpret_cast<const unsigned char*>(password.c_str()),
337 SHA256_Final(result.data(), &context);
341 } //namespace SecurityServer