Merge "fix: use EINA_* booleans instread of TRUE/FALSE" into tizen
[platform/framework/web/wrt.git] / src / view / webkit / injected-bundle / injected_bundle_uri_handling.cpp
1 /*
2  * Copyright (c) 2012 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 /*
17  * @file    injected_bundle_uri_handling.cpp
18  * @author  Marcin Kaminski (marcin.ka@samsung.com)
19  * @version 1.0
20  */
21
22 #include "injected_bundle_uri_handling.h"
23 #include <dpl/log/secure_log.h>
24 #include <string.h>
25 #include <sys/stat.h>
26 #include <dpl/utils/wrt_global_settings.h>
27 // For dao creation (widget info fetching)
28 #include <dpl/wrt-dao-ro/widget_dao_read_only.h>
29 // security checks for URI
30 #include <ace-common/ace_api_common.h>
31 #include <ace-client/ace_api_client.h>
32 #include <dpl/utils/warp_iri.h>
33 // URI localization
34 #include <dpl/localization/w3c_file_localization.h>
35 // WARP check
36 #include <widget_data_types.h>
37 #include <dpl/wrt-dao-ro/common_dao_types.h>
38 #include <dpl/scoped_ptr.h>
39 // allow-navigation check
40 #include <pcrecpp.h>
41 #include <iri.h>
42
43 namespace {
44 char const * const SCHEME_TYPE_FILE = "file";
45 char const * const SCHEME_TYPE_WIDGET = "widget";
46 char const * const SCHEME_TYPE_APP = "app";
47 char const * const SCHEME_TYPE_HTTP = "http";
48 char const * const PARAM_URL = "param:url";
49 #ifdef APP_SCHEME_ENABLED
50 char const * const ACE_IGNORED_SCHEMA[] = { "file://", "widget://", "app://",
51                                             "data:", "tel:", "sms:", "mmsto:",
52                                             "mailto:", 0 };
53 #else
54 char const * const ACE_IGNORED_SCHEMA[] = { "file://", "widget://", "data:",
55                                             "tel:", "sms:", "mmsto:", "mailto:",
56                                             0 };
57 #endif
58
59 bool wildcardCompare(std::string wildcardString, std::string target)
60 {
61     std::string re = wildcardString;
62
63     // replace special character to meaning character
64     pcrecpp::RE("\\\\").GlobalReplace("\\\\\\\\", &re);
65     pcrecpp::RE("\\.").GlobalReplace("\\\\.", &re);
66     pcrecpp::RE("\\+").GlobalReplace("\\\\+", &re);
67     pcrecpp::RE("\\?").GlobalReplace("\\\\?", &re);
68     pcrecpp::RE("\\^").GlobalReplace("\\\\^", &re);
69     pcrecpp::RE("\\$").GlobalReplace("\\\\$", &re);
70     pcrecpp::RE("\\[").GlobalReplace("\\\\[", &re);
71     pcrecpp::RE("\\]").GlobalReplace("\\\\]", &re);
72     pcrecpp::RE("\\{").GlobalReplace("\\\\{", &re);
73     pcrecpp::RE("\\}").GlobalReplace("\\\\}", &re);
74     pcrecpp::RE("\\(").GlobalReplace("\\\\(", &re);
75     pcrecpp::RE("\\)").GlobalReplace("\\\\)", &re);
76     pcrecpp::RE("\\|").GlobalReplace("\\\\|", &re);
77
78     // replace wildcard character to regex type
79     pcrecpp::RE("\\*").GlobalReplace(".*", &re);
80
81     return pcrecpp::RE(re).FullMatch(target);
82 }
83
84 bool checkWARP(const char *url, const DPL::String& tizenId)
85 {
86     // ignore WARP in test mode
87     if (GlobalSettings::WarpTestModeEnabled()) {
88         return true;
89     }
90
91     if (WarpIRI::isIRISchemaIgnored(url)) {
92         // scheme is not supported by WARP
93         return true;
94     }
95
96     WrtDB::WidgetDAOReadOnly dao = WrtDB::WidgetDAOReadOnly(tizenId);
97     WrtDB::WidgetAccessInfoList widgetAccessInfoList;
98     dao.getWidgetAccessInfo(widgetAccessInfoList);
99
100     // temporary solution for libiri parsing error
101     // This code will be removed
102     std::string urlstr = url;
103     size_t pos = urlstr.find_first_of("#?");
104     if (pos != std::string::npos) {
105         urlstr = urlstr.substr(0, pos);
106     }
107
108     return (static_cast<WidgetAccessList>(widgetAccessInfoList)).isRequiredIRI(
109                DPL::FromUTF8String(urlstr));
110 }
111
112 bool checkWhitelist(const char *url)
113 {
114     if (url == NULL) {
115         return true;
116     }
117
118     iri_t* iri = iri_parse(url);
119     if (!iri)
120         return false;
121
122     if (!iri->scheme || !iri->host || strlen(iri->host) == 0) {
123         iri_destroy(iri);
124         return true;
125     }
126
127     std::string scheme = iri->scheme;
128     std::string host = iri->host;
129     iri_destroy(iri);
130
131     if (scheme.find(SCHEME_TYPE_HTTP) == std::string::npos) {
132         return true;
133     }
134
135     return false;
136 }
137
138 bool checkAllowNavigation(const char *url, const DPL::String& tizenId)
139 {
140     if (url == NULL) {
141         return true;
142     }
143
144     DPL::ScopedPtr<iri_t> iri(iri_parse(url));
145     if (!iri->scheme || !iri->host || strlen(iri->host) == 0) {
146         return true;
147     }
148     std::string scheme = iri->scheme;
149     std::string host = iri->host;
150
151     if (scheme.find(SCHEME_TYPE_HTTP) == std::string::npos) {
152         return true;
153     }
154
155     WrtDB::WidgetDAOReadOnly dao = WrtDB::WidgetDAOReadOnly(tizenId);
156     WrtDB::WidgetAllowNavigationInfoList list;
157     dao.getWidgetAllowNavigationInfo(list);
158
159     FOREACH(it, list) {
160         if (wildcardCompare(DPL::ToUTF8String(it->scheme), scheme) &&
161             wildcardCompare(DPL::ToUTF8String(it->host), host))
162         {
163             return true;
164         }
165     }
166     _E("deny");
167     return false;
168 }
169
170 bool preventSymlink(const std::string & url)
171 {
172     if(0 != strncmp(url.c_str(), SCHEME_TYPE_FILE, strlen(SCHEME_TYPE_FILE)))
173     {
174         return true;
175     }
176
177     if(url.size() >= strlen(SCHEME_TYPE_FILE) + 3)
178     {
179         std::string file = url.substr(strlen(SCHEME_TYPE_FILE) + 3);
180         struct stat st;
181         if(0 != stat(file.c_str(), &st)) return true;
182         return !S_ISLNK(st.st_mode);
183     }
184     else
185     {
186         return true;
187     }
188 }
189
190 bool checkACE(const char* url, bool xhr, const DPL::String& tizenId)
191 {
192     if (url) {
193         for (size_t i = 0; ACE_IGNORED_SCHEMA[i]; ++i) {
194             if (0 == strncmp(url,
195                              ACE_IGNORED_SCHEMA[i],
196                              strlen(ACE_IGNORED_SCHEMA[i])))
197             {
198                 return true;
199             }
200         }
201     }
202
203     const char *devCapNamesMarkup = "externalNetworkAccess";
204     const char *devCapNamesXHR = "XMLHttpRequest";
205
206     ace_request_t aceRequest;
207
208     aceRequest.widget_handle = WrtDB::WidgetDAOReadOnly::getHandle(tizenId);
209
210     // TODO! We should get session id from somewhere (outside Widget Process)
211     const std::string session = "";
212     aceRequest.session_id = const_cast<ace_session_id_t>(session.c_str());
213     aceRequest.feature_list.count = 0;
214     aceRequest.dev_cap_list.count = 1;
215     aceRequest.dev_cap_list.items = new ace_dev_cap_t[1];
216
217     if (xhr) {
218         aceRequest.dev_cap_list.items[0].name =
219             const_cast<ace_string_t>(devCapNamesXHR);
220     } else {
221         aceRequest.dev_cap_list.items[0].name =
222             const_cast<ace_string_t>(devCapNamesMarkup);
223     }
224
225     aceRequest.dev_cap_list.items[0].param_list.count = 1;
226     aceRequest.dev_cap_list.items[0].param_list.items = new ace_param_t[1];
227     aceRequest.dev_cap_list.items[0].param_list.items[0].name =
228         const_cast<ace_string_t>(PARAM_URL);
229     aceRequest.dev_cap_list.items[0].param_list.items[0].value =
230         const_cast<ace_string_t>(url);
231
232     ace_check_result_t result = ACE_PRIVILEGE_DENIED;
233     ace_return_t ret = ace_check_access_ex(&aceRequest, &result);
234
235     _D("Result is: %d", static_cast<int>(result));
236
237     delete[] aceRequest.dev_cap_list.items[0].param_list.items;
238     delete[] aceRequest.dev_cap_list.items;
239
240     return ACE_OK == ret && ACE_ACCESS_GRANTED == result;
241 }
242 } // namespace (anonymous)
243
244 namespace InjectedBundleURIHandling {
245 bool processURI(const std::string& inputURI,
246                 const DPL::String& tizenId,
247                 WrtDB::WidgetSecurityModelVersion version)
248 {
249     if (version == WrtDB::WidgetSecurityModelVersion::WIDGET_SECURITY_MODEL_V1)
250     {
251         if (!checkWARP(inputURI.c_str(), tizenId)) {
252             _E("Request was blocked by WARP: %s", inputURI.c_str());
253             return false;
254         }
255     }
256
257     // disable for performance
258     // if (!preventSymlink(inputURI)) {
259     //     LogWarning("Request for symlink is invalid: " << inputURI);
260     //     return false;
261     //}
262
263     return true;
264 }
265
266 bool processURI(const DPL::String& inputURI,
267                 const DPL::String& tizenId,
268                 WrtDB::WidgetSecurityModelVersion version)
269 {
270     DPL::Optional<DPL::String> optionalUri(inputURI);
271     if (optionalUri.IsNull()) {
272         _D("uri is empty");
273         return true;
274     }
275
276     std::string uri = DPL::ToUTF8String(inputURI);
277     return processURI(uri, tizenId, version);
278 }
279
280 bool processMainResource(const DPL::String& inputURI,
281                          const DPL::String& tizenId,
282                          WrtDB::WidgetSecurityModelVersion version)
283 {
284     DPL::Optional<DPL::String> optionalUri(inputURI);
285     if (optionalUri.IsNull()) {
286         _D("uri is empty");
287         return true;
288     }
289
290     std::string uri = DPL::ToUTF8String(inputURI);
291     if (version ==
292         WrtDB::WidgetSecurityModelVersion::WIDGET_SECURITY_MODEL_V1)
293     {
294         if (!checkWARP(uri.c_str(), tizenId)) {
295             _E("Request was blocked by WARP: %s", uri.c_str());
296             return false;
297         }
298     } else if (version ==
299         WrtDB::WidgetSecurityModelVersion::WIDGET_SECURITY_MODEL_V2)
300     {
301 #ifdef ALLOW_NAVIGATION_ENABLED
302         if (!checkAllowNavigation(uri.c_str(), tizenId)) {
303             _E("Request was blocked by allow-navigation: %s", uri.c_str());
304             return false;
305         }
306 #else
307         return false;
308 #endif
309     }
310
311     // disable for performance
312     // if (!preventSymlink(uri)) {
313     //     LogWarning("Request for symlink is invalid: " << uri);
314     //     return false;
315     // }
316     return true;
317 }
318
319 bool processURIForPlugin(const char* url)
320 {
321     return checkWhitelist(url);
322 }
323
324 std::string localizeURI(const std::string& inputURI, const std::string& tizenId)
325 {
326     if (inputURI.compare(0, strlen(SCHEME_TYPE_WIDGET), SCHEME_TYPE_WIDGET) &&
327         inputURI.compare(0, strlen(SCHEME_TYPE_FILE), SCHEME_TYPE_FILE) &&
328         inputURI.compare(0, strlen(SCHEME_TYPE_APP), SCHEME_TYPE_APP))
329     {
330         _D("scheme doesn't need to localize");
331         return inputURI;
332     }
333
334     std::string localizedURI = W3CFileLocalization::getFilePathInWidgetPackageFromUrl(tizenId, inputURI);
335
336     if (localizedURI.empty()) {
337         return inputURI;
338     } else {
339         return std::string("file://") + localizedURI;
340     }
341 }
342
343 DPL::OptionalString localizeURI(const DPL::String& inputURI,
344                                 const DPL::String& tizenId)
345 {
346     std::string uri = DPL::ToUTF8String(inputURI);
347     const char* urlcstr = uri.c_str();
348     const char *end = strstr(urlcstr, ":");
349     if (!end) {
350         _W("no schema in link, return null");
351         return DPL::Optional<DPL::String>::Null;
352     }
353     std::string scheme(urlcstr, end);
354
355 #ifdef APP_SCHEME_ENABLED
356     if (scheme != SCHEME_TYPE_WIDGET && scheme != SCHEME_TYPE_FILE && scheme != SCHEME_TYPE_APP) {
357 #else
358     if (scheme != SCHEME_TYPE_WIDGET && scheme != SCHEME_TYPE_FILE) {
359 #endif
360         _D("scheme doesn't need to localize");
361         return DPL::OptionalString(inputURI);
362     }
363
364     DPL::Optional<DPL::String> found =
365         W3CFileLocalization::getFilePathInWidgetPackageFromUrl(
366             tizenId,
367             DPL::FromUTF8String(uri));
368
369     if (found.IsNull()) {
370         // In this case, path doesn't need to localize. return input uri
371         _W("Path not found within current locale in current widget");
372         return DPL::OptionalString(inputURI);
373     } else {
374         DPL::String uri(L"file://" + *found);
375         _D("Will load resource: %s", DPL::ToUTF8String(uri).c_str());
376         return DPL::OptionalString(uri);
377     }
378 }
379 } // namespace InjectedBundleURIHandling