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