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#if ENABLE(DATABASE)
33
34#include "PlatformString.h"
35#include <wtf/PassRefPtr.h>
36
37namespace WebCore {
38
39PassRefPtr<DatabaseAuthorizer> DatabaseAuthorizer::create(const String& databaseInfoTableName)
40{
41    return adoptRef(new DatabaseAuthorizer(databaseInfoTableName));
42}
43
44DatabaseAuthorizer::DatabaseAuthorizer(const String& databaseInfoTableName)
45    : m_securityEnabled(false)
46    , m_databaseInfoTableName(databaseInfoTableName)
47{
48    reset();
49    addWhitelistedFunctions();
50}
51
52void DatabaseAuthorizer::reset()
53{
54    m_lastActionWasInsert = false;
55    m_lastActionChangedDatabase = false;
56    m_permissions = ReadWriteMask;
57}
58
59void DatabaseAuthorizer::resetDeletes()
60{
61    m_hadDeletes = false;
62}
63
64void DatabaseAuthorizer::addWhitelistedFunctions()
65{
66    // SQLite functions used to help implement some operations
67    // ALTER TABLE helpers
68    m_whitelistedFunctions.add("sqlite_rename_table");
69    m_whitelistedFunctions.add("sqlite_rename_trigger");
70    // GLOB helpers
71    m_whitelistedFunctions.add("glob");
72
73    // SQLite core functions
74    m_whitelistedFunctions.add("abs");
75    m_whitelistedFunctions.add("changes");
76    m_whitelistedFunctions.add("coalesce");
77    m_whitelistedFunctions.add("glob");
78    m_whitelistedFunctions.add("ifnull");
79    m_whitelistedFunctions.add("hex");
80    m_whitelistedFunctions.add("last_insert_rowid");
81    m_whitelistedFunctions.add("length");
82    m_whitelistedFunctions.add("like");
83    m_whitelistedFunctions.add("lower");
84    m_whitelistedFunctions.add("ltrim");
85    m_whitelistedFunctions.add("max");
86    m_whitelistedFunctions.add("min");
87    m_whitelistedFunctions.add("nullif");
88    m_whitelistedFunctions.add("quote");
89    m_whitelistedFunctions.add("replace");
90    m_whitelistedFunctions.add("round");
91    m_whitelistedFunctions.add("rtrim");
92    m_whitelistedFunctions.add("soundex");
93    m_whitelistedFunctions.add("sqlite_source_id");
94    m_whitelistedFunctions.add("sqlite_version");
95    m_whitelistedFunctions.add("substr");
96    m_whitelistedFunctions.add("total_changes");
97    m_whitelistedFunctions.add("trim");
98    m_whitelistedFunctions.add("typeof");
99    m_whitelistedFunctions.add("upper");
100    m_whitelistedFunctions.add("zeroblob");
101
102    // SQLite date and time functions
103    m_whitelistedFunctions.add("date");
104    m_whitelistedFunctions.add("time");
105    m_whitelistedFunctions.add("datetime");
106    m_whitelistedFunctions.add("julianday");
107    m_whitelistedFunctions.add("strftime");
108
109    // SQLite aggregate functions
110    // max() and min() are already in the list
111    m_whitelistedFunctions.add("avg");
112    m_whitelistedFunctions.add("count");
113    m_whitelistedFunctions.add("group_concat");
114    m_whitelistedFunctions.add("sum");
115    m_whitelistedFunctions.add("total");
116
117    // SQLite FTS functions
118    m_whitelistedFunctions.add("match");
119    m_whitelistedFunctions.add("snippet");
120    m_whitelistedFunctions.add("offsets");
121    m_whitelistedFunctions.add("optimize");
122
123    // SQLite ICU functions
124    // like(), lower() and upper() are already in the list
125    m_whitelistedFunctions.add("regexp");
126}
127
128int DatabaseAuthorizer::createTable(const String& tableName)
129{
130    if (!allowWrite())
131        return SQLAuthDeny;
132
133    m_lastActionChangedDatabase = true;
134    return denyBasedOnTableName(tableName);
135}
136
137int DatabaseAuthorizer::createTempTable(const String& tableName)
138{
139    // SQLITE_CREATE_TEMP_TABLE results in a UPDATE operation, which is not
140    // allowed in read-only transactions or private browsing, so we might as
141    // well disallow SQLITE_CREATE_TEMP_TABLE in these cases
142    if (!allowWrite())
143        return SQLAuthDeny;
144
145    return denyBasedOnTableName(tableName);
146}
147
148int DatabaseAuthorizer::dropTable(const String& tableName)
149{
150    if (!allowWrite())
151        return SQLAuthDeny;
152
153    return updateDeletesBasedOnTableName(tableName);
154}
155
156int DatabaseAuthorizer::dropTempTable(const String& tableName)
157{
158    // SQLITE_DROP_TEMP_TABLE results in a DELETE operation, which is not
159    // allowed in read-only transactions or private browsing, so we might as
160    // well disallow SQLITE_DROP_TEMP_TABLE in these cases
161    if (!allowWrite())
162        return SQLAuthDeny;
163
164    return updateDeletesBasedOnTableName(tableName);
165}
166
167int DatabaseAuthorizer::allowAlterTable(const String&, const String& tableName)
168{
169    if (!allowWrite())
170        return SQLAuthDeny;
171
172    m_lastActionChangedDatabase = true;
173    return denyBasedOnTableName(tableName);
174}
175
176int DatabaseAuthorizer::createIndex(const String&, const String& tableName)
177{
178    if (!allowWrite())
179        return SQLAuthDeny;
180
181    m_lastActionChangedDatabase = true;
182    return denyBasedOnTableName(tableName);
183}
184
185int DatabaseAuthorizer::createTempIndex(const String&, const String& tableName)
186{
187    // SQLITE_CREATE_TEMP_INDEX should result in a UPDATE or INSERT operation,
188    // which is not allowed in read-only transactions or private browsing,
189    // so we might as well disallow SQLITE_CREATE_TEMP_INDEX in these cases
190    if (!allowWrite())
191        return SQLAuthDeny;
192
193    return denyBasedOnTableName(tableName);
194}
195
196int DatabaseAuthorizer::dropIndex(const String&, const String& tableName)
197{
198    if (!allowWrite())
199        return SQLAuthDeny;
200
201    return updateDeletesBasedOnTableName(tableName);
202}
203
204int DatabaseAuthorizer::dropTempIndex(const String&, const String& tableName)
205{
206    // SQLITE_DROP_TEMP_INDEX should result in a DELETE operation, which is
207    // not allowed in read-only transactions or private browsing, so we might
208    // as well disallow SQLITE_DROP_TEMP_INDEX in these cases
209    if (!allowWrite())
210        return SQLAuthDeny;
211
212    return updateDeletesBasedOnTableName(tableName);
213}
214
215int DatabaseAuthorizer::createTrigger(const String&, const String& tableName)
216{
217    if (!allowWrite())
218        return SQLAuthDeny;
219
220    m_lastActionChangedDatabase = true;
221    return denyBasedOnTableName(tableName);
222}
223
224int DatabaseAuthorizer::createTempTrigger(const String&, const String& tableName)
225{
226    // SQLITE_CREATE_TEMP_TRIGGER results in a INSERT operation, which is not
227    // allowed in read-only transactions or private browsing, so we might as
228    // well disallow SQLITE_CREATE_TEMP_TRIGGER in these cases
229    if (!allowWrite())
230        return SQLAuthDeny;
231
232    return denyBasedOnTableName(tableName);
233}
234
235int DatabaseAuthorizer::dropTrigger(const String&, const String& tableName)
236{
237    if (!allowWrite())
238        return SQLAuthDeny;
239
240    return updateDeletesBasedOnTableName(tableName);
241}
242
243int DatabaseAuthorizer::dropTempTrigger(const String&, const String& tableName)
244{
245    // SQLITE_DROP_TEMP_TRIGGER results in a DELETE operation, which is not
246    // allowed in read-only transactions or private browsing, so we might as
247    // well disallow SQLITE_DROP_TEMP_TRIGGER in these cases
248    if (!allowWrite())
249        return SQLAuthDeny;
250
251    return updateDeletesBasedOnTableName(tableName);
252}
253
254int DatabaseAuthorizer::createView(const String&)
255{
256    return (!allowWrite() ? SQLAuthDeny : SQLAuthAllow);
257}
258
259int DatabaseAuthorizer::createTempView(const String&)
260{
261    // SQLITE_CREATE_TEMP_VIEW results in a UPDATE operation, which is not
262    // allowed in read-only transactions or private browsing, so we might as
263    // well disallow SQLITE_CREATE_TEMP_VIEW in these cases
264    return (!allowWrite() ? SQLAuthDeny : SQLAuthAllow);
265}
266
267int DatabaseAuthorizer::dropView(const String&)
268{
269    if (!allowWrite())
270        return SQLAuthDeny;
271
272    m_hadDeletes = true;
273    return SQLAuthAllow;
274}
275
276int DatabaseAuthorizer::dropTempView(const String&)
277{
278    // SQLITE_DROP_TEMP_VIEW results in a DELETE operation, which is not
279    // allowed in read-only transactions or private browsing, so we might as
280    // well disallow SQLITE_DROP_TEMP_VIEW in these cases
281    if (!allowWrite())
282        return SQLAuthDeny;
283
284    m_hadDeletes = true;
285    return SQLAuthAllow;
286}
287
288int DatabaseAuthorizer::createVTable(const String& tableName, const String& moduleName)
289{
290    if (!allowWrite())
291        return SQLAuthDeny;
292
293    // Allow only the FTS3 extension
294    if (!equalIgnoringCase(moduleName, "fts3"))
295        return SQLAuthDeny;
296
297    m_lastActionChangedDatabase = true;
298    return denyBasedOnTableName(tableName);
299}
300
301int DatabaseAuthorizer::dropVTable(const String& tableName, const String& moduleName)
302{
303    if (!allowWrite())
304        return SQLAuthDeny;
305
306    // Allow only the FTS3 extension
307    if (!equalIgnoringCase(moduleName, "fts3"))
308        return SQLAuthDeny;
309
310    return updateDeletesBasedOnTableName(tableName);
311}
312
313int DatabaseAuthorizer::allowDelete(const String& tableName)
314{
315    if (!allowWrite())
316        return SQLAuthDeny;
317
318    return updateDeletesBasedOnTableName(tableName);
319}
320
321int DatabaseAuthorizer::allowInsert(const String& tableName)
322{
323    if (!allowWrite())
324        return SQLAuthDeny;
325
326    m_lastActionChangedDatabase = true;
327    m_lastActionWasInsert = true;
328    return denyBasedOnTableName(tableName);
329}
330
331int DatabaseAuthorizer::allowUpdate(const String& tableName, const String&)
332{
333    if (!allowWrite())
334        return SQLAuthDeny;
335
336    m_lastActionChangedDatabase = true;
337    return denyBasedOnTableName(tableName);
338}
339
340int DatabaseAuthorizer::allowTransaction()
341{
342    return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow;
343}
344
345int DatabaseAuthorizer::allowRead(const String& tableName, const String&)
346{
347    if (m_permissions & NoAccessMask && m_securityEnabled)
348        return SQLAuthDeny;
349
350    return denyBasedOnTableName(tableName);
351}
352
353int DatabaseAuthorizer::allowReindex(const String&)
354{
355    return (!allowWrite() ? SQLAuthDeny : SQLAuthAllow);
356}
357
358int DatabaseAuthorizer::allowAnalyze(const String& tableName)
359{
360    return denyBasedOnTableName(tableName);
361}
362
363int DatabaseAuthorizer::allowPragma(const String&, const String&)
364{
365    return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow;
366}
367
368int DatabaseAuthorizer::allowAttach(const String&)
369{
370    return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow;
371}
372
373int DatabaseAuthorizer::allowDetach(const String&)
374{
375    return m_securityEnabled ? SQLAuthDeny : SQLAuthAllow;
376}
377
378int DatabaseAuthorizer::allowFunction(const String& functionName)
379{
380    if (m_securityEnabled && !m_whitelistedFunctions.contains(functionName))
381        return SQLAuthDeny;
382
383    return SQLAuthAllow;
384}
385
386void DatabaseAuthorizer::disable()
387{
388    m_securityEnabled = false;
389}
390
391void DatabaseAuthorizer::enable()
392{
393    m_securityEnabled = true;
394}
395
396bool DatabaseAuthorizer::allowWrite()
397{
398    return !(m_securityEnabled && (m_permissions & ReadOnlyMask || m_permissions & NoAccessMask));
399}
400
401void DatabaseAuthorizer::setReadOnly()
402{
403    m_permissions |= ReadOnlyMask;
404}
405
406void DatabaseAuthorizer::setPermissions(int permissions)
407{
408    m_permissions = permissions;
409}
410
411int DatabaseAuthorizer::denyBasedOnTableName(const String& tableName) const
412{
413    if (!m_securityEnabled)
414        return SQLAuthAllow;
415
416    // Sadly, normal creates and drops end up affecting sqlite_master in an authorizer callback, so
417    // it will be tough to enforce all of the following policies
418    //if (equalIgnoringCase(tableName, "sqlite_master") || equalIgnoringCase(tableName, "sqlite_temp_master") ||
419    //    equalIgnoringCase(tableName, "sqlite_sequence") || equalIgnoringCase(tableName, Database::databaseInfoTableName()))
420    //        return SQLAuthDeny;
421
422    if (equalIgnoringCase(tableName, m_databaseInfoTableName))
423        return SQLAuthDeny;
424
425    return SQLAuthAllow;
426}
427
428int DatabaseAuthorizer::updateDeletesBasedOnTableName(const String& tableName)
429{
430    int allow = denyBasedOnTableName(tableName);
431    if (allow)
432        m_hadDeletes = true;
433    return allow;
434}
435
436} // namespace WebCore
437
438#endif
439