1532e3f9015f083dcd56b0300e7c23547b51a37c
[platform/core/security/security-manager.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
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;
46
47 namespace SecurityServer
48 {
49     PasswordFile::Password::Password()
50     {
51         m_password = PasswordFile::hashPassword("");
52     }
53
54     PasswordFile::Password::Password(const RawHash& password)
55     {
56         m_password = password;
57     }
58
59     PasswordFile::Password::Password(IStream& stream)
60     {
61         Deserialization::Deserialize(stream, m_password);
62     }
63
64     void PasswordFile::Password::Serialize(IStream &stream) const
65     {
66         Serialization::Serialize(stream, m_password);
67     }
68
69     PasswordFile::PasswordFile(): m_maxAttempt(0), m_historySize(0),  m_expireTime(0), m_attempt(0)
70     {
71         // check if data directory exists
72         // if not create it
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);
77             }
78         }
79
80         preparePwdFile();
81         prepareAttemptFile();
82         resetTimer();
83     }
84
85     void PasswordFile::resetTimer()
86     {
87         m_retryTimerStart = std::chrono::monotonic_clock::now();
88         m_retryTimerStart -= TimeDiff(RETRY_TIMEOUT);
89     }
90
91     void PasswordFile::preparePwdFile()
92     {
93         std::string s_pwdfilePath = DATA_DIR + "/" + PASSWORD_FILE;
94
95         // check if password file exists
96         // if not create it
97         if (!fileExists(s_pwdfilePath)) {
98             LogSecureDebug("PWD_DBG not found password file. Creating.");
99             __mode_t oldMask = umask(S_IRUSR | S_IWUSR);
100
101             //create file
102             writeMemoryToFile();
103
104             umask(oldMask);
105         } else {     //if file exists, load data
106             LogSecureDebug("PWD_DBG found password file. Opening.");
107             loadMemoryFromFile();
108         }
109     }
110
111     void PasswordFile::prepareAttemptFile()
112     {
113         std::string s_attemptfilePath = DATA_DIR + "/" + ATTEMPT_FILE;
114
115         // check if attempt file exists
116         // if not create it
117         if (!fileExists(s_attemptfilePath)) {
118             LogSecureDebug("PWD_DBG not found attempt file. Creating.");
119             __mode_t oldMask = umask(S_IRUSR | S_IWUSR);
120
121             writeAttemptToFile();
122
123             umask(oldMask);
124         } else {
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);
130             }
131
132             attemptFile.read(reinterpret_cast<char*>(&m_attempt), sizeof(unsigned int));
133             if(!attemptFile) {
134                 LogError("Failed to read attempt count.");
135                 Throw(PasswordException::FStreamReadError);
136             }
137         }
138     }
139
140     bool PasswordFile::fileExists(const std::string &filename) const
141     {
142         struct stat buf;
143
144         return ((stat(filename.c_str(), &buf) == 0));
145     }
146
147     bool PasswordFile::dirExists(const std::string &dirpath) const
148     {
149         struct stat buf;
150
151         return ((stat(dirpath.c_str(), &buf) == 0) && (((buf.st_mode) & S_IFMT) == S_IFDIR));
152     }
153
154     void PasswordFile::writeMemoryToFile() const
155     {
156         PasswordFileBuffer pwdBuffer;
157
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);
163
164         pwdBuffer.Save(DATA_DIR + "/" + PASSWORD_FILE);
165     }
166
167     void PasswordFile::loadMemoryFromFile()
168     {
169         PasswordFileBuffer pwdFile;
170
171         pwdFile.Load(DATA_DIR + "/" + PASSWORD_FILE);
172
173         m_passwords.clear();
174
175         Deserialization::Deserialize(pwdFile, m_maxAttempt);
176         Deserialization::Deserialize(pwdFile, m_historySize);
177         Deserialization::Deserialize(pwdFile, m_expireTime);
178         Deserialization::Deserialize(pwdFile, m_passwords);
179     }
180
181     void PasswordFile::writeAttemptToFile() const
182     {
183         std::ofstream attemptFile(DATA_DIR + "/" + ATTEMPT_FILE, std::ofstream::trunc);
184
185         if(!attemptFile.good()) {
186             LogError("Failed to open attempt file.");
187             Throw(PasswordException::FStreamOpenError);
188         }
189
190         attemptFile.write(reinterpret_cast<const char*>(&m_attempt), sizeof(unsigned int));
191         if(!attemptFile) {
192             LogError("Failed to write attempt count.");
193             Throw(PasswordException::FStreamWriteError);
194         }
195         attemptFile.close();
196         int fd = open((DATA_DIR + "/" + ATTEMPT_FILE).c_str(), O_WRONLY | O_APPEND); fsync(fd); close(fd);
197     }
198
199     bool PasswordFile::isPasswordActive() const
200     {
201         return !(m_passwords.empty());
202     }
203
204     void PasswordFile::setHistory(unsigned int history)
205     {
206         //setting history should be independent from password being set
207         m_historySize = history;
208
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);
212     }
213
214     unsigned int PasswordFile::getHistorySize() const
215     {
216         return m_historySize;
217     }
218
219     unsigned int PasswordFile::getAttempt() const
220     {
221         return m_attempt;
222     }
223
224     void PasswordFile::resetAttempt()
225     {
226         m_attempt = 0;
227     }
228
229     void PasswordFile::incrementAttempt()
230     {
231         m_attempt++;
232     }
233
234     int PasswordFile::getMaxAttempt() const
235     {
236         return m_maxAttempt;
237     }
238
239     void PasswordFile::setMaxAttempt(unsigned int maxAttempt)
240     {
241         m_maxAttempt = maxAttempt;
242     }
243
244     bool PasswordFile::isPasswordReused(const std::string &password) const
245     {
246         RawHash hashedPwd = hashPassword(password);
247
248         LogSecureDebug("PwdCount: " << m_passwords.size() << ", PwdMaxHistory: " << getHistorySize());
249
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!");
254             return true;
255         }
256
257         LogSecureDebug("isPasswordReused: No passwords match, password not reused.");
258         return false;
259     }
260
261     void PasswordFile::setPassword(const std::string &password)
262     {
263         RawHash hashedPwd = hashPassword(password);
264
265         m_passwords.push_front(Password(hashedPwd));
266
267         //one current password, plus history amount of passwords
268         if(m_passwords.size() > 1+getHistorySize())
269             m_passwords.pop_back();
270     }
271
272     bool PasswordFile::checkPassword(const std::string &password) const
273     {
274         RawHash hashedPwd = hashPassword(password);
275
276         return (hashedPwd == m_passwords.begin()->m_password);
277     }
278
279     void PasswordFile::setExpireTime(int expireTime)
280     {
281         if(isPasswordActive())
282             m_expireTime = expireTime;
283         else {
284             LogError("Can't set expiration time, password not active.");
285             Throw(PasswordException::PasswordNotActive);
286         }
287     }
288
289     time_t PasswordFile::getExpireTime() const
290     {
291         return m_expireTime;
292     }
293
294     time_t PasswordFile::getExpireTimeLeft() const
295     {
296         if(m_expireTime > 0)
297             return (m_expireTime - time(NULL));
298         else
299             return m_expireTime;
300     }
301
302     bool PasswordFile::checkExpiration() const
303     {
304         //return true if expired, else false
305         return ((m_expireTime != 0) && (time(NULL) > m_expireTime));
306     }
307
308     bool PasswordFile::isIgnorePeriod() const
309     {
310         TimePoint retryTimerStop = std::chrono::monotonic_clock::now();
311         TimeDiff diff = retryTimerStop - m_retryTimerStart;
312
313         m_retryTimerStart = retryTimerStop;
314
315         return (diff.count() < RETRY_TIMEOUT);
316     }
317
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)
321     {
322         RawHash result(SHA256_DIGEST_LENGTH);
323
324         SHA256_CTX context;
325         SHA256_Init(&context);
326         SHA256_Update(&context, reinterpret_cast<const unsigned char*>(password.c_str()),
327                       password.size());
328         SHA256_Final(result.data(), &context);
329
330         return result;
331     }
332 } //namespace SecurityServer
333