[BlackBerry] CredentialBackingStore implement encryptString() and decryptString()
[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 <BlackBerryPlatformClient.h>
31 #include <BlackBerryPlatformEncryptor.h>
32
33 #define HANDLE_SQL_EXEC_FAILURE(statement, returnValue, ...) \
34     if (statement) { \
35         LOG_ERROR(__VA_ARGS__); \
36         return returnValue; \
37     }
38
39 namespace WebCore {
40
41 CredentialBackingStore* CredentialBackingStore::instance()
42 {
43     static CredentialBackingStore* backingStore = 0;
44     if (!backingStore) {
45         backingStore = new CredentialBackingStore;
46         backingStore->open(pathByAppendingComponent(BlackBerry::Platform::Client::get()->getApplicationDataDirectory().c_str(), "/credentials.db"));
47     }
48     return backingStore;
49 }
50
51 CredentialBackingStore::CredentialBackingStore()
52     : m_addLoginStatement(0)
53     , m_updateLoginStatement(0)
54     , m_hasLoginStatement(0)
55     , m_getLoginStatement(0)
56     , m_getLoginByURLStatement(0)
57     , m_removeLoginStatement(0)
58     , m_addNeverRememberStatement(0)
59     , m_hasNeverRememberStatement(0)
60     , m_getNeverRememberStatement(0)
61     , m_removeNeverRememberStatement(0)
62 {
63 }
64
65 CredentialBackingStore::~CredentialBackingStore()
66 {
67     if (m_database.isOpen())
68         m_database.close();
69 }
70
71 bool CredentialBackingStore::open(const String& dbPath)
72 {
73     ASSERT(!m_database.isOpen());
74
75     HANDLE_SQL_EXEC_FAILURE(!m_database.open(dbPath), false,
76         "Failed to open database file %s for login database", dbPath.utf8().data());
77
78     if (!m_database.tableExists("logins")) {
79         HANDLE_SQL_EXEC_FAILURE(!m_database.executeCommand("CREATE TABLE logins (origin_url VARCHAR NOT NULL, host VARCHAR NOT NULL, port INTEGER, service_type INTEGER NOT NULL, realm VARCHAR, auth_scheme INTEGER NOT NULL, username VARCHAR, password BLOB) "),
80             false, "Failed to create table logins for login database");
81
82         // Create index for table logins.
83         HANDLE_SQL_EXEC_FAILURE(!m_database.executeCommand("CREATE INDEX logins_index ON logins (host)"),
84             false, "Failed to create index for table logins");
85     }
86
87     if (!m_database.tableExists("never_remember")) {
88         HANDLE_SQL_EXEC_FAILURE(!m_database.executeCommand("CREATE TABLE never_remember (origin_url VARCHAR NOT NULL, host VARCHAR NOT NULL, port INTEGER, service_type INTEGER NOT NULL, realm VARCHAR, auth_scheme INTEGER NOT NULL) "),
89             false, "Failed to create table never_remember for login database");
90
91         // Create index for table never_remember.
92         HANDLE_SQL_EXEC_FAILURE(!m_database.executeCommand("CREATE INDEX never_remember_index ON never_remember (host)"),
93             false, "Failed to create index for table never_remember");
94     }
95
96     // Prepare the statements.
97     m_addLoginStatement = new SQLiteStatement(m_database, "INSERT OR REPLACE INTO logins (origin_url, host, port, service_type, realm, auth_scheme, username, password) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
98     HANDLE_SQL_EXEC_FAILURE(m_addLoginStatement->prepare() != SQLResultOk,
99         false, "Failed to prepare addLogin statement");
100
101     m_updateLoginStatement = new SQLiteStatement(m_database, "UPDATE logins SET username = ?, password = ? WHERE origin_url = ? AND host = ? AND port = ? AND service_type = ? AND realm = ? AND auth_scheme = ?");
102     HANDLE_SQL_EXEC_FAILURE(m_updateLoginStatement->prepare() != SQLResultOk,
103         false, "Failed to prepare updateLogin statement");
104
105     m_hasLoginStatement = new SQLiteStatement(m_database, "SELECT COUNT(*) FROM logins WHERE origin_url = ? AND host = ? AND port = ? AND service_type = ? AND realm = ? AND auth_scheme = ?");
106     HANDLE_SQL_EXEC_FAILURE(m_hasLoginStatement->prepare() != SQLResultOk,
107         false, "Failed to prepare hasLogin statement");
108
109     m_getLoginStatement = new SQLiteStatement(m_database, "SELECT username, password FROM logins WHERE host = ? AND port = ? AND service_type = ? AND realm = ? AND auth_scheme = ?");
110     HANDLE_SQL_EXEC_FAILURE(m_getLoginStatement->prepare() != SQLResultOk,
111         false, "Failed to prepare getLogin statement");
112
113     m_getLoginByURLStatement = new SQLiteStatement(m_database, "SELECT username, password FROM logins WHERE origin_url = ?");
114     HANDLE_SQL_EXEC_FAILURE(m_getLoginByURLStatement->prepare() != SQLResultOk,
115         false, "Failed to prepare getLoginByURL statement");
116
117     m_removeLoginStatement = new SQLiteStatement(m_database, "DELETE FROM logins WHERE origin_url = ? AND host = ? AND port = ? AND service_type = ? AND realm = ? AND auth_scheme = ?");
118     HANDLE_SQL_EXEC_FAILURE(m_removeLoginStatement->prepare() != SQLResultOk,
119         false, "Failed to prepare removeLogin statement");
120
121     m_addNeverRememberStatement = new SQLiteStatement(m_database, "INSERT OR REPLACE INTO never_remember (origin_url, host, port, service_type, realm, auth_scheme) VALUES (?, ?, ?, ?, ?, ?)");
122     HANDLE_SQL_EXEC_FAILURE(m_addNeverRememberStatement->prepare() != SQLResultOk,
123         false, "Failed to prepare addNeverRemember statement");
124
125     m_hasNeverRememberStatement = new SQLiteStatement(m_database, "SELECT COUNT(*) FROM never_remember WHERE host = ? AND port = ? AND service_type = ? AND realm = ? AND auth_scheme = ?");
126     HANDLE_SQL_EXEC_FAILURE(m_hasNeverRememberStatement->prepare() != SQLResultOk,
127         false, "Failed to prepare hasNeverRemember statement");
128
129     m_getNeverRememberStatement = new SQLiteStatement(m_database, "SELECT origin_url FROM never_remember WHERE host = ? AND port = ? AND service_type = ? AND realm = ? AND auth_scheme = ?");
130     HANDLE_SQL_EXEC_FAILURE(m_getNeverRememberStatement->prepare() != SQLResultOk,
131         false, "Failed to prepare getNeverRemember statement");
132
133     m_removeNeverRememberStatement = new SQLiteStatement(m_database, "DELETE FROM never_remember WHERE host = ? AND port = ? AND service_type = ? AND realm = ? AND auth_scheme = ?");
134     HANDLE_SQL_EXEC_FAILURE(m_removeNeverRememberStatement->prepare() != SQLResultOk,
135         false, "Failed to prepare removeNeverRemember statement");
136
137     return true;
138 }
139
140 void CredentialBackingStore::close()
141 {
142     delete m_addLoginStatement;
143     m_addLoginStatement = 0;
144     delete m_updateLoginStatement;
145     m_updateLoginStatement = 0;
146     delete m_hasLoginStatement;
147     m_hasLoginStatement = 0;
148     delete m_getLoginStatement;
149     m_getLoginStatement = 0;
150     delete m_getLoginByURLStatement;
151     m_getLoginByURLStatement = 0;
152     delete m_removeLoginStatement;
153     m_removeLoginStatement = 0;
154     delete m_addNeverRememberStatement;
155     m_addNeverRememberStatement = 0;
156     delete m_hasNeverRememberStatement;
157     m_hasNeverRememberStatement = 0;
158     delete m_getNeverRememberStatement;
159     m_getNeverRememberStatement = 0;
160     delete m_removeNeverRememberStatement;
161     m_removeNeverRememberStatement = 0;
162
163     if (m_database.isOpen())
164         m_database.close();
165 }
166
167 bool CredentialBackingStore::addLogin(const KURL& url, const ProtectionSpace& protectionSpace, const Credential& credential)
168 {
169     ASSERT(m_database.isOpen());
170     ASSERT(m_database.tableExists("logins"));
171
172     if (!m_addLoginStatement)
173         return false;
174
175     m_addLoginStatement->bindText(1, url.string());
176     m_addLoginStatement->bindText(2, protectionSpace.host());
177     m_addLoginStatement->bindInt(3, protectionSpace.port());
178     m_addLoginStatement->bindInt(4, static_cast<int>(protectionSpace.serverType()));
179     m_addLoginStatement->bindText(5, protectionSpace.realm());
180     m_addLoginStatement->bindInt(6, static_cast<int>(protectionSpace.authenticationScheme()));
181     m_addLoginStatement->bindText(7, credential.user());
182     m_addLoginStatement->bindBlob(8, encryptedString(credential.password()));
183
184     int result = m_addLoginStatement->step();
185     m_addLoginStatement->reset();
186     HANDLE_SQL_EXEC_FAILURE(result != SQLResultDone, false,
187         "Failed to add login info into table logins - %i", result);
188
189     return true;
190 }
191
192 bool CredentialBackingStore::updateLogin(const KURL& url, const ProtectionSpace& protectionSpace, const Credential& credential)
193 {
194     ASSERT(m_database.isOpen());
195     ASSERT(m_database.tableExists("logins"));
196
197     if (!m_updateLoginStatement)
198         return false;
199
200     m_updateLoginStatement->bindText(1, credential.user());
201     m_updateLoginStatement->bindBlob(2, encryptedString(credential.password()));
202     m_updateLoginStatement->bindText(3, url.string());
203     m_updateLoginStatement->bindText(4, protectionSpace.host());
204     m_updateLoginStatement->bindInt(5, protectionSpace.port());
205     m_updateLoginStatement->bindInt(6, static_cast<int>(protectionSpace.serverType()));
206     m_updateLoginStatement->bindText(7, protectionSpace.realm());
207     m_updateLoginStatement->bindInt(8, static_cast<int>(protectionSpace.authenticationScheme()));
208
209     int result = m_updateLoginStatement->step();
210     m_updateLoginStatement->reset();
211     HANDLE_SQL_EXEC_FAILURE(result != SQLResultDone, false,
212         "Failed to update login info in table logins - %i", result);
213
214     return true;
215 }
216
217 bool CredentialBackingStore::hasLogin(const KURL& url, const ProtectionSpace& protectionSpace)
218 {
219     ASSERT(m_database.isOpen());
220     ASSERT(m_database.tableExists("logins"));
221
222     if (!m_hasLoginStatement)
223         return false;
224
225     m_hasLoginStatement->bindText(1, url.string());
226     m_hasLoginStatement->bindText(2, protectionSpace.host());
227     m_hasLoginStatement->bindInt(3, protectionSpace.port());
228     m_hasLoginStatement->bindInt(4, static_cast<int>(protectionSpace.serverType()));
229     m_hasLoginStatement->bindText(5, protectionSpace.realm());
230     m_hasLoginStatement->bindInt(6, static_cast<int>(protectionSpace.authenticationScheme()));
231
232     int result = m_hasLoginStatement->step();
233     int numOfRow = m_hasLoginStatement->getColumnInt(0);
234     m_hasLoginStatement->reset();
235     HANDLE_SQL_EXEC_FAILURE(result != SQLResultRow, false,
236         "Failed to execute select login info from table logins in hasLogin - %i", result);
237
238     if (numOfRow)
239         return true;
240     return false;
241 }
242
243 Credential CredentialBackingStore::getLogin(const ProtectionSpace& protectionSpace)
244 {
245     ASSERT(m_database.isOpen());
246     ASSERT(m_database.tableExists("logins"));
247
248     if (!m_getLoginStatement)
249         return Credential();
250
251     m_getLoginStatement->bindText(1, protectionSpace.host());
252     m_getLoginStatement->bindInt(2, protectionSpace.port());
253     m_getLoginStatement->bindInt(3, static_cast<int>(protectionSpace.serverType()));
254     m_getLoginStatement->bindText(4, protectionSpace.realm());
255     m_getLoginStatement->bindInt(5, static_cast<int>(protectionSpace.authenticationScheme()));
256
257     int result = m_getLoginStatement->step();
258     String username = m_getLoginStatement->getColumnText(0);
259     String password = m_getLoginStatement->getColumnBlobAsString(1);
260     m_getLoginStatement->reset();
261     HANDLE_SQL_EXEC_FAILURE(result != SQLResultRow, Credential(),
262         "Failed to execute select login info from table logins in getLogin - %i", result);
263
264     return Credential(username, decryptedString(password), CredentialPersistencePermanent);
265 }
266
267 Credential CredentialBackingStore::getLogin(const KURL& url)
268 {
269     ASSERT(m_database.isOpen());
270     ASSERT(m_database.tableExists("logins"));
271
272     if (!m_getLoginByURLStatement)
273         return Credential();
274
275     m_getLoginByURLStatement->bindText(1, url.string());
276
277     int result = m_getLoginByURLStatement->step();
278     String username = m_getLoginByURLStatement->getColumnText(0);
279     String password = m_getLoginByURLStatement->getColumnBlobAsString(1);
280     m_getLoginByURLStatement->reset();
281     HANDLE_SQL_EXEC_FAILURE(result != SQLResultRow, Credential(),
282         "Failed to execute select login info from table logins in getLogin - %i", result);
283
284     return Credential(username, decryptedString(password), CredentialPersistencePermanent);
285 }
286
287 bool CredentialBackingStore::removeLogin(const KURL& url, const ProtectionSpace& protectionSpace)
288 {
289     ASSERT(m_database.isOpen());
290     ASSERT(m_database.tableExists("logins"));
291
292     if (!m_removeLoginStatement)
293         return false;
294
295     m_removeLoginStatement->bindText(1, url.string());
296     m_removeLoginStatement->bindText(2, protectionSpace.host());
297     m_removeLoginStatement->bindInt(3, protectionSpace.port());
298     m_removeLoginStatement->bindInt(4, static_cast<int>(protectionSpace.serverType()));
299     m_removeLoginStatement->bindText(5, protectionSpace.realm());
300     m_removeLoginStatement->bindInt(6, static_cast<int>(protectionSpace.authenticationScheme()));
301
302     int result = m_removeLoginStatement->step();
303     m_removeLoginStatement->reset();
304     HANDLE_SQL_EXEC_FAILURE(result != SQLResultDone, false,
305         "Failed to remove login info from table logins - %i", result);
306
307     return true;
308 }
309
310 bool CredentialBackingStore::addNeverRemember(const KURL& url, const ProtectionSpace& protectionSpace)
311 {
312     ASSERT(m_database.isOpen());
313     ASSERT(m_database.tableExists("never_remember"));
314
315     if (!m_addNeverRememberStatement)
316         return false;
317
318     m_addNeverRememberStatement->bindText(1, url.string());
319     m_addNeverRememberStatement->bindText(2, protectionSpace.host());
320     m_addNeverRememberStatement->bindInt(3, protectionSpace.port());
321     m_addNeverRememberStatement->bindInt(4, static_cast<int>(protectionSpace.serverType()));
322     m_addNeverRememberStatement->bindText(5, protectionSpace.realm());
323     m_addNeverRememberStatement->bindInt(6, static_cast<int>(protectionSpace.authenticationScheme()));
324
325     int result = m_addNeverRememberStatement->step();
326     m_addNeverRememberStatement->reset();
327     HANDLE_SQL_EXEC_FAILURE(result != SQLResultDone, false,
328         "Failed to add naver saved item info into table never_remember - %i", result);
329
330     return true;
331 }
332
333 bool CredentialBackingStore::hasNeverRemember(const ProtectionSpace& protectionSpace)
334 {
335     ASSERT(m_database.isOpen());
336     ASSERT(m_database.tableExists("never_remember"));
337
338     if (!m_hasNeverRememberStatement)
339         return false;
340
341     m_hasNeverRememberStatement->bindText(1, protectionSpace.host());
342     m_hasNeverRememberStatement->bindInt(2, protectionSpace.port());
343     m_hasNeverRememberStatement->bindInt(3, static_cast<int>(protectionSpace.serverType()));
344     m_hasNeverRememberStatement->bindText(4, protectionSpace.realm());
345     m_hasNeverRememberStatement->bindInt(5, static_cast<int>(protectionSpace.authenticationScheme()));
346
347     int result = m_hasNeverRememberStatement->step();
348     int numOfRow = m_hasNeverRememberStatement->getColumnInt(0);
349     m_hasNeverRememberStatement->reset();
350     HANDLE_SQL_EXEC_FAILURE(result != SQLResultRow, false,
351         "Failed to execute select to find naver saved site from table never_remember - %i", result);
352
353     if (numOfRow)
354         return true;
355     return false;
356 }
357
358 KURL CredentialBackingStore::getNeverRemember(const ProtectionSpace& protectionSpace)
359 {
360     ASSERT(m_database.isOpen());
361     ASSERT(m_database.tableExists("never_remember"));
362
363     if (!m_getNeverRememberStatement)
364         return KURL();
365
366     m_getNeverRememberStatement->bindText(1, protectionSpace.host());
367     m_getNeverRememberStatement->bindInt(2, protectionSpace.port());
368     m_getNeverRememberStatement->bindInt(3, static_cast<int>(protectionSpace.serverType()));
369     m_getNeverRememberStatement->bindText(4, protectionSpace.realm());
370     m_getNeverRememberStatement->bindInt(5, static_cast<int>(protectionSpace.authenticationScheme()));
371
372     int result = m_getNeverRememberStatement->step();
373     String url = m_getNeverRememberStatement->getColumnText(0);
374     m_getNeverRememberStatement->reset();
375     HANDLE_SQL_EXEC_FAILURE(result != SQLResultRow, KURL(),
376         "Failed to execute select never saved site info from table never_remember in getNeverRemember - %i", result);
377
378     return KURL(ParsedURLString, url);
379 }
380
381 bool CredentialBackingStore::removeNeverRemember(const ProtectionSpace& protectionSpace)
382 {
383     ASSERT(m_database.isOpen());
384     ASSERT(m_database.tableExists("never_remember"));
385
386     if (!m_removeNeverRememberStatement)
387         return false;
388
389     m_removeNeverRememberStatement->bindText(1, protectionSpace.host());
390     m_removeNeverRememberStatement->bindInt(2, protectionSpace.port());
391     m_removeNeverRememberStatement->bindInt(3, static_cast<int>(protectionSpace.serverType()));
392     m_removeNeverRememberStatement->bindText(4, protectionSpace.realm());
393     m_removeNeverRememberStatement->bindInt(5, static_cast<int>(protectionSpace.authenticationScheme()));
394
395     int result = m_removeNeverRememberStatement->step();
396     m_removeNeverRememberStatement->reset();
397     HANDLE_SQL_EXEC_FAILURE(result != SQLResultDone, false,
398         "Failed to remove never saved site from table never_remember - %i", result);
399
400     return true;
401 }
402
403 bool CredentialBackingStore::clearLogins()
404 {
405     ASSERT(m_database.isOpen());
406     ASSERT(m_database.tableExists("logins"));
407
408     HANDLE_SQL_EXEC_FAILURE(!m_database.executeCommand("DELETE FROM logins"),
409         false, "Failed to clear table logins");
410
411     return true;
412 }
413
414 bool CredentialBackingStore::clearNeverRemember()
415 {
416     ASSERT(m_database.isOpen());
417     ASSERT(m_database.tableExists("never_remember"));
418
419     HANDLE_SQL_EXEC_FAILURE(!m_database.executeCommand("DELETE FROM never_remember"),
420         false, "Failed to clear table never_remember");
421
422     return true;
423 }
424
425 String CredentialBackingStore::encryptedString(const String& plainText) const
426 {
427     std::string cipherText;
428     BlackBerry::Platform::Encryptor::encryptString(std::string(plainText.latin1().data()), &cipherText);
429     return String(cipherText.c_str());
430 }
431
432 String CredentialBackingStore::decryptedString(const String& cipherText) const
433 {
434     std::string plainText;
435     BlackBerry::Platform::Encryptor::decryptString(std::string(cipherText.latin1().data()), &plainText);
436     return String(plainText.c_str());
437 }
438
439 } // namespace WebCore
440
441 #endif // ENABLE(BLACKBERRY_CREDENTIAL_PERSIST)