1/*
2 * Copyright (C) 2007 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30#include "DatabaseAuthorizer.h"
31
32#include <wtf/PassRefPtr.h>
33#include <wtf/text/WTFString.h>
34
35namespace WebCore {
36
37PassRefPtr<DatabaseAuthorizer> DatabaseAuthorizer::create(const String& databaseInfoTableName)
38{
39    return adoptRef(new DatabaseAuthorizer(databaseInfoTableName));
40}
41
42DatabaseAuthorizer::DatabaseAuthorizer(const String& databaseInfoTableName)
43    : m_securityEnabled(false)
44    , m_databaseInfoTableName(databaseInfoTableName)
45{
46    reset();
47    addWhitelistedFunctions();
48}
49
50void DatabaseAuthorizer::reset()
51{
52    m_lastActionWasInsert = false;
53    m_lastActionChangedDatabase = false;
54    m_permissions = ReadWriteMask;
55}
56
57void DatabaseAuthorizer::resetDeletes()
58{
59    m_hadDeletes = false;
60}
61
62void DatabaseAuthorizer::addWhitelistedFunctions()
63{
64    // SQLite functions used to help implement some operations
65    // ALTER TABLE helpers
66    m_whitelistedFunctions.add("sqlite_rename_table");
67    m_whitelistedFunctions.add("sqlite_rename_trigger");
68    // GLOB helpers
69    m_whitelistedFunctions.add("glob");
70
71    // SQLite core functions
72    m_whitelistedFunctions.add("abs");
73    m_whitelistedFunctions.add("changes");
74    m_whitelistedFunctions.add("coalesce");
75    m_whitelistedFunctions.add("glob");
76    m_whitelistedFunctions.add("ifnull");
77    m_whitelistedFunctions.add("hex");
78    m_whitelistedFunctions.add("last_insert_rowid");
79    m_whitelistedFunctions.add("length");
80    m_whitelistedFunctions.add("like");
81    m_whitelistedFunctions.add("lower");
82    m_whitelistedFunctions.add("ltrim");
83    m_whitelistedFunctions.add("max");
84    m_whitelistedFunctions.add("min");
85    m_whitelistedFunctions.add("nullif");
86    m_whitelistedFunctions.add("quote");
87    m_whitelistedFunctions.add("replace");
88    m_whitelistedFunctions.add("round");
89    m_whitelistedFunctions.add("rtrim");
90    m_whitelistedFunctions.add("soundex");
91    m_whitelistedFunctions.add("sqlite_source_id");
92    m_whitelistedFunctions.add("sqlite_version");
93    m_whitelistedFunctions.add("substr");
94    m_whitelistedFunctions.add("total_changes");
95    m_whitelistedFunctions.add("trim");
96    m_whitelistedFunctions.add("typeof");
97    m_whitelistedFunctions.add("upper");
98    m_whitelistedFunctions.add("zeroblob");
99
100    // SQLite date and time functions
101    m_whitelistedFunctions.add("date");
102    m_whitelistedFunctions.add("time");
103    m_whitelistedFunctions.add("datetime");
104    m_whitelistedFunctions.add("julianday");
105    m_whitelistedFunctions.add("strftime");
106
107    // SQLite aggregate functions
108    // max() and min() are already in the list
109    m_whitelistedFunctions.add("avg");
110    m_whitelistedFunctions.add("count");
111    m_whitelistedFunctions.add("group_concat");
112    m_whitelistedFunctions.add("sum");
113    m_whitelistedFunctions.add("total");
114
115    // SQLite FTS functions
116    m_whitelistedFunctions.add("match");
117    m_whitelistedFunctions.add("snippet");
118    m_whitelistedFunctions.add("offsets");
119    m_whitelistedFunctions.add("optimize");
120
121    // SQLite ICU functions
122    // like(), lower() and upper() are already in the list
123    m_whitelistedFunctions.add("regexp");
124}
125
126int DatabaseAuthorizer::createTable(const String& tableName)
127{
128    if (!allowWrite())
129        return SQLAuthDeny;
130
131    m_lastActionChangedDatabase = true;
132    return denyBasedOnTableName(tableName);
133}
134
135int DatabaseAuthorizer::createTempTable(const String& tableName)
136{
137    // SQLITE_CREATE_TEMP_TABLE results in a UPDATE operation, which is not
138    // allowed in read-only transactions or private browsing, so we might as
139    // well disallow SQLITE_CREATE_TEMP_TABLE in these cases
140    if (!allowWrite())
141        return SQLAuthDeny;
142
143    return denyBasedOnTableName(tableName);
144}
145
146int DatabaseAuthorizer::dropTable(const String& tableName)
147{
148    if (!allowWrite())
149        return SQLAuthDeny;
150
151    return updateDeletesBasedOnTableName(tableName);
152}
153
154int DatabaseAuthorizer::dropTempTable(const String& tableName)
155{
156    // SQLITE_DROP_TEMP_TABLE results in a DELETE operation, which is not
157    // allowed in read-only transactions or private browsing, so we might as
158    // well disallow SQLITE_DROP_TEMP_TABLE in these cases
159    if (!allowWrite())
160        return SQLAuthDeny;
161
162    return updateDeletesBasedOnTableName(tableName);
163}
164
165int DatabaseAuthorizer::allowAlterTable(const String&, const String& tableName)
166{
167    if (!allowWrite())
168        return SQLAuthDeny;
169
170    m_lastActionChangedDatabase = true;
171    return denyBasedOnTableName(tableName);
172}
173
174int DatabaseAuthorizer::createIndex(const String&, const String& tableName)
175{
176    if (!allowWrite())
177        return SQLAuthDeny;
178
179    m_lastActionChangedDatabase = true;
180    return denyBasedOnTableName(tableName);
181}
182
183int DatabaseAuthorizer::createTempIndex(const String&, const String& tableName)
184{
185    // SQLITE_CREATE_TEMP_INDEX should result in a UPDATE or INSERT operation,
186    // which is not allowed in read-only transactions or private browsing,
187    // so we might as well disallow SQLITE_CREATE_TEMP_INDEX in these cases
188    if (!allowWrite())
189        return SQLAuthDeny;
190
191    return denyBasedOnTableName(tableName);
192}
193
194int DatabaseAuthorizer::dropIndex(const String&, const String& tableName)
195{
196    if (!allowWrite())
197        return SQLAuthDeny;
198
199    return updateDeletesBasedOnTableName(tableName);
200}
201
202int DatabaseAuthorizer::dropTempIndex(const String&, const String& tableName)
203{
204    // SQLITE_DROP_TEMP_INDEX should result in a DELETE operation, which is
205    // not allowed in read-only transactions or private browsing, so we might
206    // as well disallow SQLITE_DROP_TEMP_INDEX in these cases
207    if (!allowWrite())
208        return SQLAuthDeny;
209
210    return updateDeletesBasedOnTableName(tableName);
211}
212
213int DatabaseAuthorizer::createTrigger(const String&, const String& tableName)
214{
215    if (!allowWrite())
216        return SQLAuthDeny;
217
218    m_lastActionChangedDatabase = true;
219    return denyBasedOnTableName(tableName);
220}
221
222int DatabaseAuthorizer::createTempTrigger(const String&, const String& tableName)
223{
224    // SQLITE_CREATE_TEMP_TRIGGER results in a INSERT operation, which is not
225    // allowed in read-only transactions or private browsing, so we might as
226    // well disallow SQLITE_CREATE_TEMP_TRIGGER in these cases
227    if (!allowWrite())
228        return SQLAuthDeny;
229
230    return denyBasedOnTableName(tableName);
231}
232
233int DatabaseAuthorizer::dropTrigger(const String&, const String& tableName)
234{
235    if (!allowWrite())
236        return SQLAuthDeny;
237
238    return updateDeletesBasedOnTableName(tableName);
239}
240
241int DatabaseAuthorizer::dropTempTrigger(const String&, const String& tableName)
242{
243    // SQLITE_DROP_TEMP_TRIGGER results in a DELETE operation, which is not
244    // allowed in read-only transactions or private browsing, so we might as
245    // well disallow SQLITE_DROP_TEMP_TRIGGER in these cases
246    if (!allowWrite())
247        return SQLAuthDeny;
248
249    return updateDeletesBasedOnTableName(tableName);
250}
251
252int DatabaseAuthorizer::createView(const String&)
253{
254    return (!allowWrite() ? SQLAuthDeny : SQLAuthAllow);
255}
256
257int DatabaseAuthorizer::createTempView(const String&)
258{
259    // SQLITE_CREATE_TEMP_VIEW results in a UPDATE operation, which is not
260    // allowed in read-only transactions or private browsing, so we might as
261    // well disallow SQLITE_CREATE_TEMP_VIEW in these cases
262    return (!allowWrite() ? SQLAuthDeny : SQLAuthAllow);
263}
264
265int DatabaseAuthorizer::dropView(const String&)
266{
267    if (!allowWrite())
268        return SQLAuthDeny;
269
270    m_hadDeletes = true;
271    return SQLAuthAllow;
272}
273
274int DatabaseAuthorizer::dropTempView(const String&)
275{
276    // SQLITE_DROP_TEMP_VIEW results in a DELETE operation, which is not
277    // allowed in read-only transactions or private browsing, so we might as
278    // well disallow SQLITE_DROP_TEMP_VIEW in these cases
279    if (!allowWrite())
280        return SQLAuthDeny;
281
282    m_hadDeletes = true;
283    return SQLAuthAllow;
284}
285
286int DatabaseAuthorizer::createVTable(const String& tableName, const String& moduleName)
287{
288    if (!allowWrite())
289        return SQLAuthDeny;
290
291    // Allow only the FTS3 extension
292    if (!equalIgnoringCase(moduleName, "fts3"))
293        return SQLAuthDeny;
294
295    m_lastActionChangedDatabase = true;
296    return denyBasedOnTableName(tableName);
297}
298
299int DatabaseAuthorizer::dropVTable(const String& tableName, const String& moduleName)
300{
301    if (!allowWrite())
302        return SQLAuthDeny;
303
304    // Allow only the FTS3 extension
305    if (!equalIgnoringCase(moduleName, "fts3"))
306        return SQLAuthDeny;
307
308    return updateDeletesBasedOnTableName(tableName);
309}
310
311int DatabaseAuthorizer::allowDelete(const String& tableName)
312{
313    if (!allowWrite())
314        return SQLAuthDeny;
315
316    return updateDeletesBasedOnTableName(tableName);
317}
318
319int DatabaseAuthorizer::allowInsert(const String& tableName)
320{
321    if (!allowWrite())
322        return SQLAuthDeny;
323
324    m_lastActionChangedDatabase = true;
325    m_lastActionWasInsert = true;
326    return denyBasedOnTableName(tableName);
327}
328
329int DatabaseAuthorizer::allowUpdate(const String& tableName, const String&)
330{
331    if (!allowWrite())
332        return SQLAuthDeny;
333
334    m_lastActionChangedDatabase = true;
335    return denyBasedOnTableName(tableName);
336}
337
338int DatabaseAuthorizer::allowTransaction()
339{
340    return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow;
341}
342
343int DatabaseAuthorizer::allowRead(const String& tableName, const String&)
344{
345    if (m_permissions & NoAccessMask && m_securityEnabled)
346        return SQLAuthDeny;
347
348    return denyBasedOnTableName(tableName);
349}
350
351int DatabaseAuthorizer::allowReindex(const String&)
352{
353    return (!allowWrite() ? SQLAuthDeny : SQLAuthAllow);
354}
355
356int DatabaseAuthorizer::allowAnalyze(const String& tableName)
357{
358    return denyBasedOnTableName(tableName);
359}
360
361int DatabaseAuthorizer::allowPragma(const String&, const String&)
362{
363    return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow;
364}
365
366int DatabaseAuthorizer::allowAttach(const String&)
367{
368    return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow;
369}
370
371int DatabaseAuthorizer::allowDetach(const String&)
372{
373    return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow;
374}
375
376int DatabaseAuthorizer::allowFunction(const String& functionName)
377{
378    if (m_securityEnabled && !m_whitelistedFunctions.contains(functionName))
379        return SQLAuthDeny;
380
381    return SQLAuthAllow;
382}
383
384void DatabaseAuthorizer::disable()
385{
386    m_securityEnabled = false;
387}
388
389void DatabaseAuthorizer::enable()
390{
391    m_securityEnabled = true;
392}
393
394bool DatabaseAuthorizer::allowWrite()
395{
396    return !(m_securityEnabled && (m_permissions & ReadOnlyMask || m_permissions & NoAccessMask));
397}
398
399void DatabaseAuthorizer::setReadOnly()
400{
401    m_permissions |= ReadOnlyMask;
402}
403
404void DatabaseAuthorizer::setPermissions(int permissions)
405{
406    m_permissions = permissions;
407}
408
409int DatabaseAuthorizer::denyBasedOnTableName(const String& tableName) const
410{
411    if (!m_securityEnabled)
412        return SQLAuthAllow;
413
414    // Sadly, normal creates and drops end up affecting sqlite_master in an authorizer callback, so
415    // it will be tough to enforce all of the following policies
416    //if (equalIgnoringCase(tableName, "sqlite_master") || equalIgnoringCase(tableName, "sqlite_temp_master") ||
417    //    equalIgnoringCase(tableName, "sqlite_sequence") || equalIgnoringCase(tableName, Database::databaseInfoTableName()))
418    //        return SQLAuthDeny;
419
420    if (equalIgnoringCase(tableName, m_databaseInfoTableName))
421        return SQLAuthDeny;
422
423    return SQLAuthAllow;
424}
425
426int DatabaseAuthorizer::updateDeletesBasedOnTableName(const String& tableName)
427{
428    int allow = denyBasedOnTableName(tableName);
429    if (allow)
430        m_hadDeletes = true;
431    return allow;
432}
433
434} // namespace WebCore
435