79cc5496bde0892f13a479c5e23d8a3511e69529
[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  * @author      Piotr Bartosiewicz (p.bartosiewi@partner.samsung.com)
23  * @version     1.0
24  * @brief       Implementation of PasswordFile, used to manage password files.
25  */
26 #include <password-file.h>
27
28 #include <fstream>
29 #include <algorithm>
30 #include <limits>
31
32 #include <fcntl.h>
33 #include <string.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36
37 #include <openssl/sha.h>
38
39 #include <dpl/log/log.h>
40 #include <dpl/fstream_accessors.h>
41
42 #include <security-server.h>
43 #include <protocols.h>
44 #include <password-exception.h>
45 #include <password-file-buffer.h>
46
47 namespace {
48     const std::string DATA_DIR = "/opt/data/security-server";
49     const std::string PASSWORD_FILE = DATA_DIR + "/password";
50     const std::string OLD_VERSION_PASSWORD_FILE = DATA_DIR + "/password.pwd";
51     const std::string ATTEMPT_FILE = DATA_DIR + "/attempt";
52     const double RETRY_TIMEOUT = 0.5;
53     const mode_t FILE_MODE = S_IRUSR | S_IWUSR;
54     const unsigned int CURRENT_FILE_VERSION = 3;
55 } // namespace anonymous
56
57 namespace SecurityServer
58 {
59     const time_t PASSWORD_INFINITE_EXPIRATION_TIME = std::numeric_limits<time_t>::max();
60
61     class NoPassword: public IPassword
62     {
63         public:
64             NoPassword(IStream&) {}
65             NoPassword() {}
66
67             void Serialize(IStream &stream) const
68             {
69                 Serialization::Serialize(stream, static_cast<unsigned int>(PasswordType::NONE));
70             }
71
72             bool match(const std::string &) const
73             {
74                 return false;
75             }
76     };
77
78     class SHA256Password: public IPassword
79     {
80         public:
81             SHA256Password(IStream& stream)
82             {
83                 Deserialization::Deserialize(stream, m_hash);
84             }
85
86             SHA256Password(const std::string &password)
87                 : m_hash(hash(password)) {}
88
89             SHA256Password(const RawHash& hash)
90                 : m_hash(hash) {}
91
92             void Serialize(IStream &stream) const
93             {
94                 Serialization::Serialize(stream, static_cast<unsigned int>(PasswordType::SHA256));
95                 Serialization::Serialize(stream, m_hash);
96             }
97
98             bool match(const std::string &password) const
99             {
100                 return m_hash == hash(password);
101             }
102         private:
103             RawHash m_hash;
104
105             static RawHash hash(const std::string &password)
106             {
107                 RawHash result(SHA256_DIGEST_LENGTH);
108
109                 SHA256_CTX context;
110                 SHA256_Init(&context);
111                 SHA256_Update(&context, reinterpret_cast<const unsigned char*>(password.c_str()),
112                         password.size());
113                 SHA256_Final(result.data(), &context);
114
115                 return result;
116             }
117     };
118
119     // deserialization of new password format
120     template <>
121     void Deserialization::Deserialize(IStream& stream, IPasswordPtr& ptr)
122     {
123         unsigned int algorithm;
124         Deserialization::Deserialize(stream, algorithm);
125         switch (algorithm) {
126             case IPassword::PasswordType::NONE:
127                 ptr.reset(new NoPassword());
128                 break;
129             case IPassword::PasswordType::SHA256:
130                 ptr.reset(new SHA256Password(stream));
131                 break;
132             default:
133                 Throw(PasswordException::FStreamReadError);
134         }
135     }
136
137     PasswordFile::PasswordFile(): m_passwordCurrent(new NoPassword()),
138                                   m_maxAttempt(PASSWORD_INFINITE_ATTEMPT_COUNT),
139                                   m_maxHistorySize(0),
140                                   m_expireTime(PASSWORD_INFINITE_EXPIRATION_TIME),
141                                   m_passwordActive(false), m_attempt(0)
142     {
143         // check if data directory exists
144         // if not create it
145         if (!dirExists(DATA_DIR.c_str())) {
146             if(mkdir(DATA_DIR.c_str(), 0700)) {
147                 LogError("Failed to create directory for files. Error: " << strerror(errno));
148                 Throw(PasswordException::MakeDirError);
149             }
150         }
151
152         preparePwdFile();
153         prepareAttemptFile();
154         resetTimer();
155     }
156
157     void PasswordFile::resetState()
158     {
159         m_passwordCurrent.reset(new NoPassword());
160         m_maxAttempt = PASSWORD_INFINITE_ATTEMPT_COUNT;
161         m_maxHistorySize = 0;
162         m_expireTime = PASSWORD_INFINITE_EXPIRATION_TIME;
163         m_passwordActive = false;
164     }
165
166     void PasswordFile::resetTimer()
167     {
168         m_retryTimerStart = ClockType::now();
169         m_retryTimerStart -= TimeDiff(RETRY_TIMEOUT);
170     }
171
172     void PasswordFile::preparePwdFile()
173     {
174         // check if password file exists
175         if (!fileExists(PASSWORD_FILE)) {
176             // if old format file exist - load it
177             if (tryLoadMemoryFromOldFormatFile()) {
178                 // save in new format
179                 writeMemoryToFile();
180                 // and remove old file
181                 remove(OLD_VERSION_PASSWORD_FILE.c_str());
182                 return;
183             }
184
185             LogSecureDebug("PWD_DBG not found password file. Creating.");
186
187             //create file
188             writeMemoryToFile();
189         } else {     //if file exists, load data
190             LogSecureDebug("PWD_DBG found password file. Opening.");
191             try {
192                 loadMemoryFromFile();
193             } catch (...) {
194                 LogError("Invalid " << PASSWORD_FILE << " file format");
195                 resetState();
196                 writeMemoryToFile();
197             }
198         }
199     }
200
201     void PasswordFile::prepareAttemptFile()
202     {
203         // check if attempt file exists
204         // if not create it
205         if (!fileExists(ATTEMPT_FILE)) {
206             LogSecureDebug("PWD_DBG not found attempt file. Creating.");
207
208             writeAttemptToFile();
209         } else {
210             LogSecureDebug("PWD_DBG found attempt file. Opening.");
211             std::ifstream attemptFile(ATTEMPT_FILE);
212             if(!attemptFile) {
213                 LogError("Failed to open attempt file.");
214                 // ignore error
215                 return;
216             }
217
218             attemptFile.read(reinterpret_cast<char*>(&m_attempt), sizeof(unsigned int));
219             if(!attemptFile) {
220                 LogError("Failed to read attempt count.");
221                 // ignore error
222                 resetAttempt();
223             }
224         }
225     }
226
227     bool PasswordFile::fileExists(const std::string &filename) const
228     {
229         struct stat buf;
230
231         return ((stat(filename.c_str(), &buf) == 0));
232     }
233
234     bool PasswordFile::dirExists(const std::string &dirpath) const
235     {
236         struct stat buf;
237
238         return ((stat(dirpath.c_str(), &buf) == 0) && (((buf.st_mode) & S_IFMT) == S_IFDIR));
239     }
240
241     void PasswordFile::writeMemoryToFile() const
242     {
243         PasswordFileBuffer pwdBuffer;
244
245         LogSecureDebug("Saving max_att: " << m_maxAttempt << ", history_size: " <<
246                        m_maxHistorySize << ", m_expireTime: " << m_expireTime << ", isActive: " <<
247                        m_passwordActive);
248
249         //serialize password attributes
250         Serialization::Serialize(pwdBuffer, CURRENT_FILE_VERSION);
251         Serialization::Serialize(pwdBuffer, m_maxAttempt);
252         Serialization::Serialize(pwdBuffer, m_maxHistorySize);
253         Serialization::Serialize(pwdBuffer, m_expireTime);
254         Serialization::Serialize(pwdBuffer, m_passwordActive);
255         Serialization::Serialize(pwdBuffer, m_passwordCurrent);
256         Serialization::Serialize(pwdBuffer, m_passwordHistory);
257
258         pwdBuffer.Save(PASSWORD_FILE);
259
260         chmod(PASSWORD_FILE.c_str(), FILE_MODE);
261     }
262
263     void PasswordFile::loadMemoryFromFile()
264     {
265         PasswordFileBuffer pwdFile;
266
267         pwdFile.Load(PASSWORD_FILE);
268
269         unsigned int fileVersion = 0;
270         Deserialization::Deserialize(pwdFile, fileVersion);
271         if (fileVersion != CURRENT_FILE_VERSION)
272             Throw(PasswordException::FStreamReadError);
273
274         m_passwordHistory.clear();
275
276         Deserialization::Deserialize(pwdFile, m_maxAttempt);
277         Deserialization::Deserialize(pwdFile, m_maxHistorySize);
278         Deserialization::Deserialize(pwdFile, m_expireTime);
279         Deserialization::Deserialize(pwdFile, m_passwordActive);
280         Deserialization::Deserialize(pwdFile, m_passwordCurrent);
281         Deserialization::Deserialize(pwdFile, m_passwordHistory);
282
283         LogSecureDebug("Loaded max_att: " << m_maxAttempt << ", history_size: " <<
284                        m_maxHistorySize << ", m_expireTime: " << m_expireTime << ", isActive: " <<
285                        m_passwordActive);
286     }
287
288     bool PasswordFile::tryLoadMemoryFromOldFormatFile()
289     {
290         struct stat oldFileStat;
291         if (stat(OLD_VERSION_PASSWORD_FILE.c_str(), &oldFileStat) != 0)
292             return false;
293
294         static const int ELEMENT_SIZE = sizeof(unsigned) + SHA256_DIGEST_LENGTH;
295         static const int VERSION_1_REMAINING = sizeof(unsigned) * 4;
296         static const int VERSION_2_REMAINING = VERSION_1_REMAINING + sizeof(bool);
297         int remaining = oldFileStat.st_size % ELEMENT_SIZE;
298
299         if (remaining != VERSION_1_REMAINING && remaining != VERSION_2_REMAINING)
300             return false;
301
302         try {
303             PasswordFileBuffer pwdFile;
304             pwdFile.Load(OLD_VERSION_PASSWORD_FILE);
305
306             Deserialization::Deserialize(pwdFile, m_maxAttempt);
307             Deserialization::Deserialize(pwdFile, m_maxHistorySize);
308             Deserialization::Deserialize(pwdFile, m_expireTime);
309             if (m_expireTime == 0)
310                 m_expireTime = PASSWORD_INFINITE_EXPIRATION_TIME;
311             if (remaining == VERSION_2_REMAINING)
312                 Deserialization::Deserialize(pwdFile, m_passwordActive);
313             else
314                 m_passwordActive = true;
315
316             // deserialize passwords in old format
317             struct OldPassword {
318                 OldPassword() {}
319                 OldPassword(IStream &stream)
320                 {
321                     Deserialization::Deserialize(stream, m_hash);
322                 }
323                 IPassword::RawHash m_hash;
324             };
325             std::list<OldPassword> oldFormatPasswords;
326             Deserialization::Deserialize(pwdFile, oldFormatPasswords);
327
328             // convert passwords to new format
329             m_passwordHistory.clear();
330             if (oldFormatPasswords.empty()) {
331                 m_passwordCurrent.reset(new NoPassword());
332                 m_passwordActive = false;
333             } else {
334                 m_passwordCurrent.reset(new SHA256Password(oldFormatPasswords.front().m_hash));
335                 std::for_each(++oldFormatPasswords.begin(), oldFormatPasswords.end(),
336                         [&] (const OldPassword& pwd)
337                         {m_passwordHistory.push_back(IPasswordPtr(new SHA256Password(pwd.m_hash)));}
338                         );
339             }
340         } catch (...) {
341             LogWarning("Invalid " << OLD_VERSION_PASSWORD_FILE << " file format");
342             resetState();
343             return false;
344         }
345
346         return true;
347     }
348
349     void PasswordFile::writeAttemptToFile() const
350     {
351         std::ofstream attemptFile(ATTEMPT_FILE, std::ofstream::trunc);
352
353         if(!attemptFile.good()) {
354             LogError("Failed to open attempt file.");
355             Throw(PasswordException::FStreamOpenError);
356         }
357
358         attemptFile.write(reinterpret_cast<const char*>(&m_attempt), sizeof(unsigned int));
359         if(!attemptFile) {
360             LogError("Failed to write attempt count.");
361             Throw(PasswordException::FStreamWriteError);
362         }
363
364         attemptFile.flush();
365         fsync(DPL::FstreamAccessors<std::ofstream>::GetFd(attemptFile)); // flush kernel space buffer
366         attemptFile.close();
367     }
368
369     void PasswordFile::activatePassword()
370     {
371         m_passwordActive = true;
372     }
373
374     bool PasswordFile::isPasswordActive() const
375     {
376         return m_passwordActive;
377     }
378
379     void PasswordFile::setMaxHistorySize(unsigned int history)
380     {
381         //setting history should be independent from password being set
382         m_maxHistorySize = history;
383
384         while(m_passwordHistory.size() > history)
385             m_passwordHistory.pop_back();
386     }
387
388     unsigned int PasswordFile::getMaxHistorySize() const
389     {
390         return m_maxHistorySize;
391     }
392
393     unsigned int PasswordFile::getAttempt() const
394     {
395         return m_attempt;
396     }
397
398     void PasswordFile::resetAttempt()
399     {
400         m_attempt = 0;
401     }
402
403     void PasswordFile::incrementAttempt()
404     {
405         m_attempt++;
406     }
407
408     int PasswordFile::getMaxAttempt() const
409     {
410         return m_maxAttempt;
411     }
412
413     void PasswordFile::setMaxAttempt(unsigned int maxAttempt)
414     {
415         m_maxAttempt = maxAttempt;
416     }
417
418     bool PasswordFile::isPasswordReused(const std::string &password) const
419     {
420         LogSecureDebug("Checking if pwd is reused. HistorySize: " << m_passwordHistory.size() <<
421                        ", MaxHistorySize: " << getMaxHistorySize());
422
423         //go through history and check if password existed earlier
424         if(std::any_of(m_passwordHistory.begin(), m_passwordHistory.end(),
425                        [&password](const IPasswordPtr& pwd) { return pwd->match(password); })) {
426             LogSecureDebug("Passwords match!");
427             return true;
428         }
429
430         LogSecureDebug("isPasswordReused: No passwords match, password not reused.");
431         return false;
432     }
433
434     void PasswordFile::setPassword(const std::string &password)
435     {
436         //put current password to history
437         m_passwordHistory.push_front(std::move(m_passwordCurrent));
438
439         //erase last password if we exceed max history size
440         if(m_passwordHistory.size() > getMaxHistorySize())
441             m_passwordHistory.pop_back();
442
443         //replace current password with new one
444         m_passwordCurrent.reset(new SHA256Password(password));
445     }
446
447     bool PasswordFile::checkPassword(const std::string &password) const
448     {
449         return m_passwordCurrent->match(password);
450     }
451
452     void PasswordFile::setExpireTime(time_t expireTime)
453     {
454         if(isPasswordActive())
455             m_expireTime = expireTime;
456         else {
457             LogError("Can't set expiration time, password not active.");
458             Throw(PasswordException::PasswordNotActive);
459         }
460     }
461
462     unsigned int PasswordFile::getExpireTimeLeft() const
463     {
464         if(m_expireTime != PASSWORD_INFINITE_EXPIRATION_TIME) {
465             time_t timeLeft = m_expireTime - time(NULL);
466             return (timeLeft < 0) ? 0 : static_cast<unsigned int>(timeLeft);
467         } else
468             return PASSWORD_API_NO_EXPIRATION;
469     }
470
471     bool PasswordFile::checkExpiration() const
472     {
473         //return true if expired, else false
474         return ((m_expireTime != PASSWORD_INFINITE_EXPIRATION_TIME) && (time(NULL) > m_expireTime));
475     }
476
477     bool PasswordFile::checkIfAttemptsExceeded() const
478     {
479         return ((m_maxAttempt != PASSWORD_INFINITE_ATTEMPT_COUNT) && (m_attempt > m_maxAttempt));
480     }
481
482     bool PasswordFile::isIgnorePeriod() const
483     {
484         TimePoint retryTimerStop = ClockType::now();
485         TimeDiff diff = retryTimerStop - m_retryTimerStart;
486
487         m_retryTimerStart = retryTimerStop;
488
489         return (diff.count() < RETRY_TIMEOUT);
490     }
491
492     bool PasswordFile::isHistoryActive() const
493     {
494         return (m_maxHistorySize != 0);
495     }
496 } //namespace SecurityServer
497