0e19587179a594ba6084f7a6c0a9a0267388a671
[WebKit-https.git] / Source / WebCore / platform / network / blackberry / CredentialBackingStore.cpp
1 /*
2  * Copyright (C) 2011 Research In Motion Limited. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 #include "config.h"
20
21 #if ENABLE(BLACKBERRY_CREDENTIAL_PERSIST)
22 #include "CredentialBackingStore.h"
23
24 #include "CredentialStorage.h"
25 #include "FileSystem.h"
26 #include "KURL.h"
27 #include "NotImplemented.h"
28 #include "ProtectionSpaceHash.h"
29 #include "SQLiteStatement.h"
30 #include <BlackBerryPlatformEncryptor.h>
31 #include <BlackBerryPlatformSettings.h>
32 #include <CertMgrWrapper.h>
33
34 #define HANDLE_SQL_EXEC_FAILURE(statement, returnValue, ...) \
35     if (statement) { \
36         LOG_ERROR(__VA_ARGS__); \
37         return returnValue; \
38     }
39
40 namespace WebCore {
41
42 static unsigned hashCredentialInfo(const ProtectionSpace& space, const String& username)
43 {
44     String hashString = String::format("%s@%s@%d@%d@%s@%d",
45         username.utf8().data(),
46         space.host().utf8().data(), space.port(),
47         static_cast<int>(space.serverType()),
48         space.realm().utf8().data(),
49         static_cast<int>(space.authenticationScheme()));
50     return StringHasher::computeHashAndMaskTop8Bits(hashString.characters(), hashString.length());
51 }
52
53 CredentialBackingStore& credentialBackingStore()
54 {
55     DEFINE_STATIC_LOCAL(CredentialBackingStore, backingStore, ());
56     if (!backingStore.m_database.isOpen())
57         backingStore.open(pathByAppendingComponent(BlackBerry::Platform::Settings::instance()->applicationDataDirectory().c_str(), "/credentials.db"));
58     return backingStore;
59 }
60
61 CredentialBackingStore::CredentialBackingStore()
62     : m_addLoginStatement(0)
63     , m_updateLoginStatement(0)
64     , m_hasLoginStatement(0)
65     , m_getLoginStatement(0)
66     , m_removeLoginStatement(0)
67     , m_addNeverRememberStatement(0)
68     , m_hasNeverRememberStatement(0)
69     , m_removeNeverRememberStatement(0)
70     , m_certMgrWrapper(0)
71 {
72 }
73
74 CredentialBackingStore::~CredentialBackingStore()
75 {
76     delete m_certMgrWrapper;
77     m_certMgrWrapper = 0;
78     delete m_addLoginStatement;
79     m_addLoginStatement = 0;
80     delete m_updateLoginStatement;
81     m_updateLoginStatement = 0;
82     delete m_hasLoginStatement;
83     m_hasLoginStatement = 0;
84     delete m_getLoginStatement;
85     m_getLoginStatement = 0;
86     delete m_removeLoginStatement;
87     m_removeLoginStatement = 0;
88     delete m_addNeverRememberStatement;
89     m_addNeverRememberStatement = 0;
90     delete m_hasNeverRememberStatement;
91     m_hasNeverRememberStatement = 0;
92     delete m_removeNeverRememberStatement;
93     m_removeNeverRememberStatement = 0;
94
95     if (m_database.isOpen())
96         m_database.close();
97 }
98
99 bool CredentialBackingStore::open(const String& dbPath)
100 {
101     ASSERT(!m_database.isOpen());
102
103     HANDLE_SQL_EXEC_FAILURE(!m_database.open(dbPath), false,
104         "Failed to open database file %s for login database", dbPath.utf8().data());
105
106     if (!m_database.tableExists("logins")) {
107         HANDLE_SQL_EXEC_FAILURE(!m_database.executeCommand("CREATE TABLE logins (host VARCHAR NOT NULL, port INTEGER, service_type INTEGER NOT NULL, realm VARCHAR, auth_scheme INTEGER NOT NULL, username VARCHAR, password BLOB) "),
108             false, "Failed to create table logins for login database");
109
110         // Create index for table logins.
111         HANDLE_SQL_EXEC_FAILURE(!m_database.executeCommand("CREATE INDEX logins_index ON logins (host)"),
112             false, "Failed to create index for table logins");
113     }
114
115     if (!m_database.tableExists("never_remember")) {
116         HANDLE_SQL_EXEC_FAILURE(!m_database.executeCommand("CREATE TABLE never_remember (host VARCHAR NOT NULL, port INTEGER, service_type INTEGER NOT NULL, realm VARCHAR, auth_scheme INTEGER NOT NULL) "),
117             false, "Failed to create table never_remember for login database");
118
119         // Create index for table never_remember.
120         HANDLE_SQL_EXEC_FAILURE(!m_database.executeCommand("CREATE INDEX never_remember_index ON never_remember (host)"),
121             false, "Failed to create index for table never_remember");
122     }
123
124     // Prepare the statements.
125     m_addLoginStatement = new SQLiteStatement(m_database, "INSERT OR REPLACE INTO logins (host, port, service_type, realm, auth_scheme, username, password) VALUES (?, ?, ?, ?, ?, ?, ?)");
126     HANDLE_SQL_EXEC_FAILURE(m_addLoginStatement->prepare() != SQLResultOk,
127         false, "Failed to prepare addLogin statement");
128
129     m_updateLoginStatement = new SQLiteStatement(m_database, "UPDATE logins SET username = ?, password = ? WHERE host = ? AND port = ? AND service_type = ? AND realm = ? AND auth_scheme = ?");
130     HANDLE_SQL_EXEC_FAILURE(m_updateLoginStatement->prepare() != SQLResultOk,
131         false, "Failed to prepare updateLogin statement");
132
133     m_hasLoginStatement = new SQLiteStatement(m_database, "SELECT COUNT(*) FROM logins WHERE host = ? AND port = ? AND service_type = ? AND realm = ? AND auth_scheme = ?");
134     HANDLE_SQL_EXEC_FAILURE(m_hasLoginStatement->prepare() != SQLResultOk,
135         false, "Failed to prepare hasLogin statement");
136
137     m_getLoginStatement = new SQLiteStatement(m_database, "SELECT username, password FROM logins WHERE host = ? AND port = ? AND service_type = ? AND realm = ? AND auth_scheme = ?");
138     HANDLE_SQL_EXEC_FAILURE(m_getLoginStatement->prepare() != SQLResultOk,
139         false, "Failed to prepare getLogin statement");
140
141     m_removeLoginStatement = new SQLiteStatement(m_database, "DELETE FROM logins WHERE host = ? AND port = ? AND service_type = ? AND realm = ? AND auth_scheme = ?");
142     HANDLE_SQL_EXEC_FAILURE(m_removeLoginStatement->prepare() != SQLResultOk,
143         false, "Failed to prepare removeLogin statement");
144
145     m_addNeverRememberStatement = new SQLiteStatement(m_database, "INSERT OR REPLACE INTO never_remember (host, port, service_type, realm, auth_scheme) VALUES (?, ?, ?, ?, ?)");
146     HANDLE_SQL_EXEC_FAILURE(m_addNeverRememberStatement->prepare() != SQLResultOk,
147         false, "Failed to prepare addNeverRemember statement");
148
149     m_hasNeverRememberStatement = new SQLiteStatement(m_database, "SELECT COUNT(*) FROM never_remember WHERE host = ? AND port = ? AND service_type = ? AND realm = ? AND auth_scheme = ?");
150     HANDLE_SQL_EXEC_FAILURE(m_hasNeverRememberStatement->prepare() != SQLResultOk,
151         false, "Failed to prepare hasNeverRemember statement");
152
153     m_removeNeverRememberStatement = new SQLiteStatement(m_database, "DELETE FROM never_remember WHERE host = ? AND port = ? AND service_type = ? AND realm = ? AND auth_scheme = ?");
154     HANDLE_SQL_EXEC_FAILURE(m_removeNeverRememberStatement->prepare() != SQLResultOk,
155         false, "Failed to prepare removeNeverRemember statement");
156
157     return true;
158 }
159
160 bool CredentialBackingStore::addLogin(const ProtectionSpace& protectionSpace, const Credential& credential)
161 {
162     ASSERT(m_database.isOpen());
163     ASSERT(m_database.tableExists("logins"));
164
165     if (!m_addLoginStatement)
166         return false;
167
168     m_addLoginStatement->bindText(1, protectionSpace.host());
169     m_addLoginStatement->bindInt(2, protectionSpace.port());
170     m_addLoginStatement->bindInt(3, static_cast<int>(protectionSpace.serverType()));
171     m_addLoginStatement->bindText(4, protectionSpace.realm());
172     m_addLoginStatement->bindInt(5, static_cast<int>(protectionSpace.authenticationScheme()));
173     m_addLoginStatement->bindText(6, credential.user());
174     if (certMgrWrapper()->isReady())
175         m_addLoginStatement->bindBlob(7, "");
176     else {
177         String ciphertext = encryptedString(credential.password());
178         ASSERT(ciphertext.is8Bit());
179         m_addLoginStatement->bindBlob(7, ciphertext.characters8(), ciphertext.length());
180     }
181
182     int result = m_addLoginStatement->step();
183     m_addLoginStatement->reset();
184     HANDLE_SQL_EXEC_FAILURE(result != SQLResultDone, false,
185         "Failed to add login info into table logins - %i", result);
186
187     if (!certMgrWrapper()->isReady())
188         return true;
189
190     String ciphertext = encryptedString(credential.password());
191     ASSERT(ciphertext.is8Bit());
192     unsigned hash = hashCredentialInfo(protectionSpace, credential.user());
193     return certMgrWrapper()->savePassword(hash, std::string(reinterpret_cast<const char*>(ciphertext.characters8()), ciphertext.length()));
194 }
195
196 bool CredentialBackingStore::updateLogin(const ProtectionSpace& protectionSpace, const Credential& credential)
197 {
198     ASSERT(m_database.isOpen());
199     ASSERT(m_database.tableExists("logins"));
200
201     if (!m_updateLoginStatement)
202         return false;
203
204     m_updateLoginStatement->bindText(1, credential.user());
205     if (certMgrWrapper()->isReady())
206         m_updateLoginStatement->bindBlob(2, "");
207     else {
208         String ciphertext = encryptedString(credential.password());
209         ASSERT(ciphertext.is8Bit());
210         m_updateLoginStatement->bindBlob(2, ciphertext.characters8(), ciphertext.length());
211     }
212     m_updateLoginStatement->bindText(3, protectionSpace.host());
213     m_updateLoginStatement->bindInt(4, protectionSpace.port());
214     m_updateLoginStatement->bindInt(5, static_cast<int>(protectionSpace.serverType()));
215     m_updateLoginStatement->bindText(6, protectionSpace.realm());
216     m_updateLoginStatement->bindInt(7, static_cast<int>(protectionSpace.authenticationScheme()));
217
218     int result = m_updateLoginStatement->step();
219     m_updateLoginStatement->reset();
220     HANDLE_SQL_EXEC_FAILURE(result != SQLResultDone, false,
221         "Failed to update login info in table logins - %i", result);
222
223     if (!certMgrWrapper()->isReady())
224         return true;
225
226     String ciphertext = encryptedString(credential.password());
227     ASSERT(ciphertext.is8Bit());
228     unsigned hash = hashCredentialInfo(protectionSpace, credential.user());
229     return certMgrWrapper()->savePassword(hash, std::string(reinterpret_cast<const char*>(ciphertext.characters8()), ciphertext.length()));
230 }
231
232 bool CredentialBackingStore::hasLogin(const ProtectionSpace& protectionSpace)
233 {
234     ASSERT(m_database.isOpen());
235     ASSERT(m_database.tableExists("logins"));
236
237     if (!m_hasLoginStatement)
238         return false;
239
240     m_hasLoginStatement->bindText(1, protectionSpace.host());
241     m_hasLoginStatement->bindInt(2, protectionSpace.port());
242     m_hasLoginStatement->bindInt(3, static_cast<int>(protectionSpace.serverType()));
243     m_hasLoginStatement->bindText(4, protectionSpace.realm());
244     m_hasLoginStatement->bindInt(5, static_cast<int>(protectionSpace.authenticationScheme()));
245
246     int result = m_hasLoginStatement->step();
247     int numOfRow = m_hasLoginStatement->getColumnInt(0);
248     m_hasLoginStatement->reset();
249     HANDLE_SQL_EXEC_FAILURE(result != SQLResultRow, false,
250         "Failed to execute select login info from table logins in hasLogin - %i", result);
251
252     if (numOfRow)
253         return true;
254     return false;
255 }
256
257 Credential CredentialBackingStore::getLogin(const ProtectionSpace& protectionSpace)
258 {
259     ASSERT(m_database.isOpen());
260     ASSERT(m_database.tableExists("logins"));
261
262     if (!m_getLoginStatement)
263         return Credential();
264
265     m_getLoginStatement->bindText(1, protectionSpace.host());
266     m_getLoginStatement->bindInt(2, protectionSpace.port());
267     m_getLoginStatement->bindInt(3, static_cast<int>(protectionSpace.serverType()));
268     m_getLoginStatement->bindText(4, protectionSpace.realm());
269     m_getLoginStatement->bindInt(5, static_cast<int>(protectionSpace.authenticationScheme()));
270
271     int result = m_getLoginStatement->step();
272     String username = m_getLoginStatement->getColumnText(0);
273     String password = m_getLoginStatement->getColumnBlobAsString(1);
274     m_getLoginStatement->reset();
275     HANDLE_SQL_EXEC_FAILURE(result != SQLResultRow, Credential(),
276         "Failed to execute select login info from table logins in getLogin - %i", result);
277
278     if (!certMgrWrapper()->isReady())
279         return Credential(username, decryptedString(password), CredentialPersistencePermanent);
280
281     unsigned hash = hashCredentialInfo(protectionSpace, username);
282
283     std::string passwordBlob;
284     if (!certMgrWrapper()->getPassword(hash, passwordBlob))
285         return Credential();
286
287     return Credential(username, decryptedString(String(passwordBlob.data(), passwordBlob.length())), CredentialPersistencePermanent);
288 }
289
290 bool CredentialBackingStore::removeLogin(const ProtectionSpace& protectionSpace, const String& username)
291 {
292     ASSERT(m_database.isOpen());
293     ASSERT(m_database.tableExists("logins"));
294
295     if (!m_removeLoginStatement)
296         return false;
297
298     m_removeLoginStatement->bindText(1, protectionSpace.host());
299     m_removeLoginStatement->bindInt(2, protectionSpace.port());
300     m_removeLoginStatement->bindInt(3, static_cast<int>(protectionSpace.serverType()));
301     m_removeLoginStatement->bindText(4, protectionSpace.realm());
302     m_removeLoginStatement->bindInt(5, static_cast<int>(protectionSpace.authenticationScheme()));
303
304     int result = m_removeLoginStatement->step();
305     m_removeLoginStatement->reset();
306     HANDLE_SQL_EXEC_FAILURE(result != SQLResultDone, false,
307         "Failed to remove login info from table logins - %i", result);
308
309     if (!certMgrWrapper()->isReady())
310         return true;
311
312     unsigned hash = hashCredentialInfo(protectionSpace, username);
313     if (!certMgrWrapper()->removePassword(hash))
314         return false;
315
316     return true;
317 }
318
319 bool CredentialBackingStore::addNeverRemember(const ProtectionSpace& protectionSpace)
320 {
321     ASSERT(m_database.isOpen());
322     ASSERT(m_database.tableExists("never_remember"));
323
324     if (!m_addNeverRememberStatement)
325         return false;
326
327     m_addNeverRememberStatement->bindText(1, protectionSpace.host());
328     m_addNeverRememberStatement->bindInt(2, protectionSpace.port());
329     m_addNeverRememberStatement->bindInt(3, static_cast<int>(protectionSpace.serverType()));
330     m_addNeverRememberStatement->bindText(4, protectionSpace.realm());
331     m_addNeverRememberStatement->bindInt(5, static_cast<int>(protectionSpace.authenticationScheme()));
332
333     int result = m_addNeverRememberStatement->step();
334     m_addNeverRememberStatement->reset();
335     HANDLE_SQL_EXEC_FAILURE(result != SQLResultDone, false,
336         "Failed to add naver saved item info into table never_remember - %i", result);
337
338     return true;
339 }
340
341 bool CredentialBackingStore::hasNeverRemember(const ProtectionSpace& protectionSpace)
342 {
343     ASSERT(m_database.isOpen());
344     ASSERT(m_database.tableExists("never_remember"));
345
346     if (!m_hasNeverRememberStatement)
347         return false;
348
349     m_hasNeverRememberStatement->bindText(1, protectionSpace.host());
350     m_hasNeverRememberStatement->bindInt(2, protectionSpace.port());
351     m_hasNeverRememberStatement->bindInt(3, static_cast<int>(protectionSpace.serverType()));
352     m_hasNeverRememberStatement->bindText(4, protectionSpace.realm());
353     m_hasNeverRememberStatement->bindInt(5, static_cast<int>(protectionSpace.authenticationScheme()));
354
355     int result = m_hasNeverRememberStatement->step();
356     int numOfRow = m_hasNeverRememberStatement->getColumnInt(0);
357     m_hasNeverRememberStatement->reset();
358     HANDLE_SQL_EXEC_FAILURE(result != SQLResultRow, false,
359         "Failed to execute select to find naver saved site from table never_remember - %i", result);
360
361     if (numOfRow)
362         return true;
363     return false;
364 }
365
366 bool CredentialBackingStore::removeNeverRemember(const ProtectionSpace& protectionSpace)
367 {
368     ASSERT(m_database.isOpen());
369     ASSERT(m_database.tableExists("never_remember"));
370
371     if (!m_removeNeverRememberStatement)
372         return false;
373
374     m_removeNeverRememberStatement->bindText(1, protectionSpace.host());
375     m_removeNeverRememberStatement->bindInt(2, protectionSpace.port());
376     m_removeNeverRememberStatement->bindInt(3, static_cast<int>(protectionSpace.serverType()));
377     m_removeNeverRememberStatement->bindText(4, protectionSpace.realm());
378     m_removeNeverRememberStatement->bindInt(5, static_cast<int>(protectionSpace.authenticationScheme()));
379
380     int result = m_removeNeverRememberStatement->step();
381     m_removeNeverRememberStatement->reset();
382     HANDLE_SQL_EXEC_FAILURE(result != SQLResultDone, false,
383         "Failed to remove never saved site from table never_remember - %i", result);
384
385     return true;
386 }
387
388 bool CredentialBackingStore::clearLogins()
389 {
390     ASSERT(m_database.isOpen());
391     ASSERT(m_database.tableExists("logins"));
392
393     HANDLE_SQL_EXEC_FAILURE(!m_database.executeCommand("DELETE FROM logins"),
394         false, "Failed to clear table logins");
395
396     return true;
397 }
398
399 bool CredentialBackingStore::clearNeverRemember()
400 {
401     ASSERT(m_database.isOpen());
402     ASSERT(m_database.tableExists("never_remember"));
403
404     HANDLE_SQL_EXEC_FAILURE(!m_database.executeCommand("DELETE FROM never_remember"),
405         false, "Failed to clear table never_remember");
406
407     return true;
408 }
409
410 String CredentialBackingStore::encryptedString(const String& plainText) const
411 {
412     WTF::CString utf8 = plainText.utf8(true);
413     std::string cipherText;
414     BlackBerry::Platform::Encryptor::encryptString(std::string(utf8.data(), utf8.length()), &cipherText);
415     return String(cipherText.data(), cipherText.length());
416 }
417
418 String CredentialBackingStore::decryptedString(const String& cipherText) const
419 {
420     std::string text = cipherText.is8Bit() ?
421         std::string(reinterpret_cast<const char*>(cipherText.characters8()), cipherText.length() * sizeof(LChar)) :
422         std::string(reinterpret_cast<const char*>(cipherText.characters16()), cipherText.length() * sizeof(UChar));
423
424     std::string plainText;
425     BlackBerry::Platform::Encryptor::decryptString(text, &plainText);
426
427     return String::fromUTF8(plainText.data(), plainText.length());
428 }
429
430 BlackBerry::Platform::CertMgrWrapper* CredentialBackingStore::certMgrWrapper()
431 {
432     if (!m_certMgrWrapper)
433         m_certMgrWrapper = new BlackBerry::Platform::CertMgrWrapper();
434
435     return m_certMgrWrapper;
436 }
437
438
439 } // namespace WebCore
440
441 #endif // ENABLE(BLACKBERRY_CREDENTIAL_PERSIST)