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>
42 const std::string DATA_DIR = "/opt/data/security-server";
43 const std::string PASSWORD_FILE = "password.pwd";
44 const std::string ATTEMPT_FILE = "attempt";
45 const double RETRY_TIMEOUT = 0.5;
47 namespace SecurityServer
49 PasswordFile::Password::Password()
51 m_password = PasswordFile::hashPassword("");
54 PasswordFile::Password::Password(const RawHash& password)
56 m_password = password;
59 PasswordFile::Password::Password(IStream& stream)
61 Deserialization::Deserialize(stream, m_password);
64 void PasswordFile::Password::Serialize(IStream &stream) const
66 Serialization::Serialize(stream, m_password);
69 PasswordFile::PasswordFile(): m_maxAttempt(0), m_historySize(0), m_expireTime(0), m_attempt(0)
71 // check if data directory exists
73 if (!dirExists(DATA_DIR.c_str())) {
74 if(mkdir(DATA_DIR.c_str(), 0700)) {
75 LogError("Failed to create directory for files. Error: " << strerror(errno));
76 Throw(PasswordException::MakeDirError);
85 void PasswordFile::resetTimer()
87 m_retryTimerStart = std::chrono::monotonic_clock::now();
88 m_retryTimerStart -= TimeDiff(RETRY_TIMEOUT);
91 void PasswordFile::preparePwdFile()
93 std::string s_pwdfilePath = DATA_DIR + "/" + PASSWORD_FILE;
95 // check if password file exists
97 if (!fileExists(s_pwdfilePath)) {
98 LogSecureDebug("PWD_DBG not found password file. Creating.");
99 __mode_t oldMask = umask(S_IRUSR | S_IWUSR);
105 } else { //if file exists, load data
106 LogSecureDebug("PWD_DBG found password file. Opening.");
107 loadMemoryFromFile();
111 void PasswordFile::prepareAttemptFile()
113 std::string s_attemptfilePath = DATA_DIR + "/" + ATTEMPT_FILE;
115 // check if attempt file exists
117 if (!fileExists(s_attemptfilePath)) {
118 LogSecureDebug("PWD_DBG not found attempt file. Creating.");
119 __mode_t oldMask = umask(S_IRUSR | S_IWUSR);
121 writeAttemptToFile();
125 LogSecureDebug("PWD_DBG found attempt file. Opening.");
126 std::ifstream attemptFile(s_attemptfilePath);
127 if(!attemptFile.good()) {
128 LogError("Failed to open attempt file.");
129 Throw(PasswordException::FStreamOpenError);
132 attemptFile.read(reinterpret_cast<char*>(&m_attempt), sizeof(unsigned int));
134 LogError("Failed to read attempt count.");
135 Throw(PasswordException::FStreamReadError);
140 bool PasswordFile::fileExists(const std::string &filename) const
144 return ((stat(filename.c_str(), &buf) == 0));
147 bool PasswordFile::dirExists(const std::string &dirpath) const
151 return ((stat(dirpath.c_str(), &buf) == 0) && (((buf.st_mode) & S_IFMT) == S_IFDIR));
154 void PasswordFile::writeMemoryToFile() const
156 PasswordFileBuffer pwdBuffer;
158 //serialize password attributes
159 Serialization::Serialize(pwdBuffer, m_maxAttempt);
160 Serialization::Serialize(pwdBuffer, m_historySize);
161 Serialization::Serialize(pwdBuffer, m_expireTime);
162 Serialization::Serialize(pwdBuffer, m_passwords);
164 pwdBuffer.Save(DATA_DIR + "/" + PASSWORD_FILE);
167 void PasswordFile::loadMemoryFromFile()
169 PasswordFileBuffer pwdFile;
171 pwdFile.Load(DATA_DIR + "/" + PASSWORD_FILE);
175 Deserialization::Deserialize(pwdFile, m_maxAttempt);
176 Deserialization::Deserialize(pwdFile, m_historySize);
177 Deserialization::Deserialize(pwdFile, m_expireTime);
178 Deserialization::Deserialize(pwdFile, m_passwords);
181 void PasswordFile::writeAttemptToFile() const
183 std::ofstream attemptFile(DATA_DIR + "/" + ATTEMPT_FILE, std::ofstream::trunc);
185 if(!attemptFile.good()) {
186 LogError("Failed to open attempt file.");
187 Throw(PasswordException::FStreamOpenError);
190 attemptFile.write(reinterpret_cast<const char*>(&m_attempt), sizeof(unsigned int));
192 LogError("Failed to write attempt count.");
193 Throw(PasswordException::FStreamWriteError);
196 int fd = open((DATA_DIR + "/" + ATTEMPT_FILE).c_str(), O_WRONLY | O_APPEND); fsync(fd); close(fd);
199 bool PasswordFile::isPasswordActive() const
201 return !(m_passwords.empty());
204 void PasswordFile::setHistory(unsigned int history)
206 //setting history should be independent from password being set
207 m_historySize = history;
209 //we want to keep 1 current pwd, plus history amount of passwords.
210 if(m_passwords.size() > 1+history)
211 m_passwords.resize(1+history);
214 unsigned int PasswordFile::getHistorySize() const
216 return m_historySize;
219 unsigned int PasswordFile::getAttempt() const
224 void PasswordFile::resetAttempt()
229 void PasswordFile::incrementAttempt()
234 int PasswordFile::getMaxAttempt() const
239 void PasswordFile::setMaxAttempt(unsigned int maxAttempt)
241 m_maxAttempt = maxAttempt;
244 bool PasswordFile::isPasswordReused(const std::string &password) const
246 RawHash hashedPwd = hashPassword(password);
248 LogSecureDebug("PwdCount: " << m_passwords.size() << ", PwdMaxHistory: " << getHistorySize());
250 if(std::find_if(m_passwords.begin(), m_passwords.end(),
251 [&hashedPwd](const Password& pwd) { return (pwd.m_password == hashedPwd); })
252 != m_passwords.end()) {
253 LogSecureDebug("Passwords match!");
257 LogSecureDebug("isPasswordReused: No passwords match, password not reused.");
261 void PasswordFile::setPassword(const std::string &password)
263 RawHash hashedPwd = hashPassword(password);
265 m_passwords.push_front(Password(hashedPwd));
267 //one current password, plus history amount of passwords
268 if(m_passwords.size() > 1+getHistorySize())
269 m_passwords.pop_back();
272 bool PasswordFile::checkPassword(const std::string &password) const
274 RawHash hashedPwd = hashPassword(password);
276 return (hashedPwd == m_passwords.begin()->m_password);
279 void PasswordFile::setExpireTime(int expireTime)
281 if(isPasswordActive())
282 m_expireTime = expireTime;
284 LogError("Can't set expiration time, password not active.");
285 Throw(PasswordException::PasswordNotActive);
289 time_t PasswordFile::getExpireTime() const
294 time_t PasswordFile::getExpireTimeLeft() const
297 return (m_expireTime - time(NULL));
302 bool PasswordFile::checkExpiration() const
304 //return true if expired, else false
305 return ((m_expireTime != 0) && (time(NULL) > m_expireTime));
308 bool PasswordFile::isIgnorePeriod() const
310 TimePoint retryTimerStop = std::chrono::monotonic_clock::now();
311 TimeDiff diff = retryTimerStop - m_retryTimerStart;
313 m_retryTimerStart = retryTimerStop;
315 return (diff.count() < RETRY_TIMEOUT);
318 //hashPassword is also used in Password struct constructor, that's why it's static. Moreover
319 //it is assumed that incorrect input password was checked earlier.
320 PasswordFile::RawHash PasswordFile::hashPassword(const std::string &password)
322 RawHash result(SHA256_DIGEST_LENGTH);
325 SHA256_Init(&context);
326 SHA256_Update(&context, reinterpret_cast<const unsigned char*>(password.c_str()),
328 SHA256_Final(result.data(), &context);
332 } //namespace SecurityServer