0cef5002a53c877e0b5a6782048bcd94f9916283
[platform/core/security/security-server.git] / src / server / service / password-file.cpp
1 /*
2  *  Copyright (c) 2000 - 2013 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *  Contact: Bumjin Im <bj.im@samsung.com>
5  *
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
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
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
17  */
18 /*
19  * @file        password-file.cpp
20  * @author      Zbigniew Jasinski (z.jasinski@samsung.com)
21  * @author      Lukasz Kostyra (l.kostyra@partner.samsung.com)
22  * @version     1.0
23  * @brief       Implementation of PasswordFile, used to manage password files.
24  */
25 #include <password-file.h>
26
27 #include <fstream>
28 #include <algorithm>
29
30 #include <openssl/sha.h>
31
32 #include <sys/stat.h>
33
34 #include <dpl/log/log.h>
35
36 #include <security-server.h>
37 #include <password-exception.h>
38 #include <password-file-buffer.h>
39
40 #include <fcntl.h>
41 #include <string.h>
42
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;
47
48 namespace SecurityServer
49 {
50     PasswordFile::Password::Password()
51     {
52         m_password = PasswordFile::hashPassword("");
53     }
54
55     PasswordFile::Password::Password(const RawHash& password)
56     {
57         m_password = password;
58     }
59
60     PasswordFile::Password::Password(IStream& stream)
61     {
62         Deserialization::Deserialize(stream, m_password);
63     }
64
65     void PasswordFile::Password::Serialize(IStream &stream) const
66     {
67         Serialization::Serialize(stream, m_password);
68     }
69
70     PasswordFile::PasswordFile(): m_maxAttempt(0), m_historySize(0),  m_expireTime(0), m_attempt(0)
71     {
72         // check if data directory exists
73         // if not create it
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);
78             }
79         }
80
81         preparePwdFile();
82         prepareAttemptFile();
83         resetTimer();
84     }
85
86     void PasswordFile::resetTimer()
87     {
88         m_retryTimerStart = std::chrono::monotonic_clock::now();
89         m_retryTimerStart -= TimeDiff(RETRY_TIMEOUT);
90     }
91
92     void PasswordFile::preparePwdFile()
93     {
94         std::string s_pwdfilePath = DATA_DIR + "/" + PASSWORD_FILE;
95
96         // check if password file exists
97         // if not create it
98         if (!fileExists(s_pwdfilePath)) {
99             LogSecureDebug("PWD_DBG not found password file. Creating.");
100             __mode_t oldMask = umask(S_IRUSR | S_IWUSR);
101
102             //create file
103             writeMemoryToFile();
104
105             umask(oldMask);
106         } else {     //if file exists, load data
107             LogSecureDebug("PWD_DBG found password file. Opening.");
108             loadMemoryFromFile();
109         }
110     }
111
112     void PasswordFile::prepareAttemptFile()
113     {
114         std::string s_attemptfilePath = DATA_DIR + "/" + ATTEMPT_FILE;
115
116         // check if attempt file exists
117         // if not create it
118         if (!fileExists(s_attemptfilePath)) {
119             LogSecureDebug("PWD_DBG not found attempt file. Creating.");
120             __mode_t oldMask = umask(S_IRUSR | S_IWUSR);
121
122             writeAttemptToFile();
123
124             umask(oldMask);
125         } else {
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);
131             }
132
133             attemptFile.read(reinterpret_cast<char*>(&m_attempt), sizeof(unsigned int));
134             if(!attemptFile) {
135                 LogError("Failed to read attempt count.");
136                 Throw(PasswordException::FStreamReadError);
137             }
138         }
139     }
140
141     bool PasswordFile::fileExists(const std::string &filename) const
142     {
143         struct stat buf;
144
145         return ((stat(filename.c_str(), &buf) == 0));
146     }
147
148     bool PasswordFile::dirExists(const std::string &dirpath) const
149     {
150         struct stat buf;
151
152         return ((stat(dirpath.c_str(), &buf) == 0) && (((buf.st_mode) & S_IFMT) == S_IFDIR));
153     }
154
155     void PasswordFile::writeMemoryToFile() const
156     {
157         PasswordFileBuffer pwdBuffer;
158
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);
164
165         pwdBuffer.Save(DATA_DIR + "/" + PASSWORD_FILE);
166     }
167
168     void PasswordFile::loadMemoryFromFile()
169     {
170         PasswordFileBuffer pwdFile;
171
172         pwdFile.Load(DATA_DIR + "/" + PASSWORD_FILE);
173
174         m_passwords.clear();
175
176         Deserialization::Deserialize(pwdFile, m_maxAttempt);
177         Deserialization::Deserialize(pwdFile, m_historySize);
178         Deserialization::Deserialize(pwdFile, m_expireTime);
179         Deserialization::Deserialize(pwdFile, m_passwords);
180     }
181
182     void PasswordFile::writeAttemptToFile() const
183     {
184         std::ofstream attemptFile(DATA_DIR + "/" + ATTEMPT_FILE, std::ofstream::trunc);
185
186         if(!attemptFile.good()) {
187             LogError("Failed to open attempt file.");
188             Throw(PasswordException::FStreamOpenError);
189         }
190
191         attemptFile.write(reinterpret_cast<const char*>(&m_attempt), sizeof(unsigned int));
192         if(!attemptFile) {
193             LogError("Failed to write attempt count.");
194             Throw(PasswordException::FStreamWriteError);
195         }
196         attemptFile.close();
197
198         int fd;
199         if (0 <= (fd = open((DATA_DIR + "/" + ATTEMPT_FILE).c_str(), O_WRONLY | O_APPEND))) {
200             fsync(fd); // force synchronization system buffers with file
201             close(fd);
202         } else {
203             int err = errno;
204             LogError("Failed to sync attempt file: " << DATA_DIR << "/" << ATTEMPT_FILE << "strerror: " << strerror(err));
205         }
206     }
207
208     bool PasswordFile::isPasswordActive() const
209     {
210         return !(m_passwords.empty());
211     }
212
213     void PasswordFile::setHistory(unsigned int history)
214     {
215         //setting history should be independent from password being set
216         m_historySize = history;
217
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);
221     }
222
223     unsigned int PasswordFile::getHistorySize() const
224     {
225         return m_historySize;
226     }
227
228     unsigned int PasswordFile::getAttempt() const
229     {
230         return m_attempt;
231     }
232
233     void PasswordFile::resetAttempt()
234     {
235         m_attempt = 0;
236     }
237
238     void PasswordFile::incrementAttempt()
239     {
240         m_attempt++;
241     }
242
243     int PasswordFile::getMaxAttempt() const
244     {
245         return m_maxAttempt;
246     }
247
248     void PasswordFile::setMaxAttempt(unsigned int maxAttempt)
249     {
250         m_maxAttempt = maxAttempt;
251     }
252
253     bool PasswordFile::isPasswordReused(const std::string &password) const
254     {
255         RawHash hashedPwd = hashPassword(password);
256
257         LogSecureDebug("PwdCount: " << m_passwords.size() << ", PwdMaxHistory: " << getHistorySize());
258
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!");
263             return true;
264         }
265
266         LogSecureDebug("isPasswordReused: No passwords match, password not reused.");
267         return false;
268     }
269
270     void PasswordFile::setPassword(const std::string &password)
271     {
272         RawHash hashedPwd = hashPassword(password);
273
274         m_passwords.push_front(Password(hashedPwd));
275
276         //one current password, plus history amount of passwords
277         if(m_passwords.size() > 1+getHistorySize())
278             m_passwords.pop_back();
279     }
280
281     bool PasswordFile::checkPassword(const std::string &password) const
282     {
283         RawHash hashedPwd = hashPassword(password);
284
285         return (hashedPwd == m_passwords.begin()->m_password);
286     }
287
288     void PasswordFile::setExpireTime(int expireTime)
289     {
290         if(isPasswordActive())
291             m_expireTime = expireTime;
292         else {
293             LogError("Can't set expiration time, password not active.");
294             Throw(PasswordException::PasswordNotActive);
295         }
296     }
297
298     time_t PasswordFile::getExpireTime() const
299     {
300         return m_expireTime;
301     }
302
303     time_t PasswordFile::getExpireTimeLeft() const
304     {
305         if(m_expireTime > 0)
306             return (m_expireTime - time(NULL));
307         else
308             return m_expireTime;
309     }
310
311     bool PasswordFile::checkExpiration() const
312     {
313         //return true if expired, else false
314         return ((m_expireTime != 0) && (time(NULL) > m_expireTime));
315     }
316
317     bool PasswordFile::isIgnorePeriod() const
318     {
319         TimePoint retryTimerStop = std::chrono::monotonic_clock::now();
320         TimeDiff diff = retryTimerStop - m_retryTimerStart;
321
322         m_retryTimerStart = retryTimerStop;
323
324         return (diff.count() < RETRY_TIMEOUT);
325     }
326
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)
330     {
331         RawHash result(SHA256_DIGEST_LENGTH);
332
333         SHA256_CTX context;
334         SHA256_Init(&context);
335         SHA256_Update(&context, reinterpret_cast<const unsigned char*>(password.c_str()),
336                       password.size());
337         SHA256_Final(result.data(), &context);
338
339         return result;
340     }
341 } //namespace SecurityServer
342