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