[Release] wrt_0.8.240
[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/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 // WhiteList check
43 #include <dpl/wrt-dao-ro/global_dao_read_only.h>
44 // WKBundle API (i.e. message sending)
45 #include <WKBundle.h>
46 #include <WKString.h>
47 #include <WKType.h>
48
49 namespace {
50 char const * const SCHEME_TYPE_FILE = "file";
51 char const * const SCHEME_TYPE_WIDGET = "widget";
52 char const * const SCHEME_TYPE_APP = "app";
53 char const * const SCHEME_TYPE_HTTP = "http";
54 char const * const WARP_ERROR_MSG =
55     "file:///usr/etc/wrt/warp_security_error.msg";
56 char const * const PARAM_URL = "param:url";
57 #ifdef APP_SCHEME_ENABLED
58 char const * const ACE_IGNORED_SCHEMA[] = { "file://", "widget://", "app://",
59                                             "data:", "tel:", "sms:", "mmsto:",
60                                             "mailto:", 0 };
61 #else
62 char const * const ACE_IGNORED_SCHEMA[] = { "file://", "widget://", "data:",
63                                             "tel:", "sms:", "mmsto:", "mailto:",
64                                             0 };
65 #endif
66
67 bool wildcardCompare(std::string wildcardString, std::string target)
68 {
69     std::string re = wildcardString;
70
71     // replace special character to meaning character
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     pcrecpp::RE("\\$").GlobalReplace("\\\\$", &re);
78     pcrecpp::RE("\\[").GlobalReplace("\\\\[", &re);
79     pcrecpp::RE("\\]").GlobalReplace("\\\\]", &re);
80     pcrecpp::RE("\\{").GlobalReplace("\\\\{", &re);
81     pcrecpp::RE("\\}").GlobalReplace("\\\\}", &re);
82     pcrecpp::RE("\\(").GlobalReplace("\\\\(", &re);
83     pcrecpp::RE("\\)").GlobalReplace("\\\\)", &re);
84     pcrecpp::RE("\\|").GlobalReplace("\\\\|", &re);
85
86     // replace wildcard character to regex type
87     pcrecpp::RE("\\*").GlobalReplace(".*", &re);
88
89     return pcrecpp::RE(re).FullMatch(target);
90 }
91
92 bool checkWARP(const char *url, const DPL::String& tizenId)
93 {
94     // ignore WARP in test mode
95     if (GlobalSettings::WarpTestModeEnabled()) {
96         return true;
97     }
98
99     if (WarpIRI::isIRISchemaIgnored(url)) {
100         // scheme is not supported by WARP
101         return true;
102     }
103
104     WrtDB::WidgetDAOReadOnly dao = WrtDB::WidgetDAOReadOnly(tizenId);
105     WrtDB::WidgetAccessInfoList widgetAccessInfoList;
106     dao.getWidgetAccessInfo(widgetAccessInfoList);
107
108     // temporary solution for libiri parsing error
109     // This code will be removed
110     std::string urlstr = url;
111     size_t pos = urlstr.find_first_of("#?");
112     if (pos != std::string::npos) {
113         urlstr = urlstr.substr(0, pos);
114     }
115
116     return (static_cast<WidgetAccessList>(widgetAccessInfoList)).isRequiredIRI(
117                DPL::FromUTF8String(urlstr));
118 }
119
120 bool checkWhitelist(const char *url)
121 {
122     LogInfo("Check WhiteList");
123     if (url == NULL) {
124         return true;
125     }
126
127     DPL::ScopedPtr<iri_t> iri(iri_parse(url));
128     if (!iri->scheme || !iri->host || strlen(iri->host) == 0) {
129         return true;
130     }
131     std::string scheme = iri->scheme;
132     std::string host = iri->host;
133
134     if (scheme.find(SCHEME_TYPE_HTTP) == std::string::npos) {
135         LogDebug("url doesn't need to check white list");
136         return true;
137     }
138
139     WidgetAccessList whiteURIList(WrtDB::GlobalDAOReadOnly::GetWhiteURIList());
140     return whiteURIList.isRequiredIRI(DPL::FromUTF8String(host));
141 }
142
143 bool checkAllowNavigation(const char *url, const DPL::String& tizenId)
144 {
145     if (url == NULL) {
146         return true;
147     }
148
149     DPL::ScopedPtr<iri_t> iri(iri_parse(url));
150     if (!iri->scheme || !iri->host || strlen(iri->host) == 0) {
151         return true;
152     }
153     std::string scheme = iri->scheme;
154     std::string host = iri->host;
155
156     if (scheme.find(SCHEME_TYPE_HTTP) == std::string::npos) {
157         LogDebug("url doesn't need to check allow-navigation");
158         return true;
159     }
160
161     WrtDB::WidgetDAOReadOnly dao = WrtDB::WidgetDAOReadOnly(tizenId);
162     WrtDB::WidgetAllowNavigationInfoList list;
163     dao.getWidgetAllowNavigationInfo(list);
164
165     FOREACH(it, list) {
166         if (wildcardCompare(DPL::ToUTF8String(it->scheme), scheme) &&
167             wildcardCompare(DPL::ToUTF8String(it->host), host))
168         {
169             return true;
170         }
171     }
172     LogDebug("deny");
173     return false;
174 }
175
176 bool preventSymlink(const std::string & url)
177 {
178     if(0 != strncmp(url.c_str(), SCHEME_TYPE_FILE, strlen(SCHEME_TYPE_FILE)))
179     {
180         return true;
181     }
182     if(url.size() >= strlen(SCHEME_TYPE_FILE) + 3)
183     {
184         std::string file = url.substr(strlen(SCHEME_TYPE_FILE) + 3);
185         struct stat st;
186         if(0 != stat(file.c_str(), &st)) return true;
187         return !S_ISLNK(st.st_mode);
188     }
189     else
190     {
191         return true;
192     }
193 }
194
195 bool checkACE(const char* url, bool xhr, const DPL::String& tizenId)
196 {
197     if (url) {
198         for (size_t i = 0; ACE_IGNORED_SCHEMA[i]; ++i) {
199             if (0 == strncmp(url,
200                              ACE_IGNORED_SCHEMA[i],
201                              strlen(ACE_IGNORED_SCHEMA[i])))
202             {
203                 return true;
204             }
205         }
206     }
207
208     const char *devCapNamesMarkup = "externalNetworkAccess";
209     const char *devCapNamesXHR = "XMLHttpRequest";
210
211     ace_request_t aceRequest;
212
213     aceRequest.widget_handle = WrtDB::WidgetDAOReadOnly::getHandle(tizenId);
214
215     // TODO! We should get session id from somewhere (outside Widget Process)
216     const std::string session = "";
217     aceRequest.session_id = const_cast<ace_session_id_t>(session.c_str());
218     aceRequest.feature_list.count = 0;
219     aceRequest.dev_cap_list.count = 1;
220     aceRequest.dev_cap_list.items = new ace_dev_cap_t[1];
221
222     if (xhr) {
223         aceRequest.dev_cap_list.items[0].name =
224             const_cast<ace_string_t>(devCapNamesXHR);
225     } else {
226         aceRequest.dev_cap_list.items[0].name =
227             const_cast<ace_string_t>(devCapNamesMarkup);
228     }
229
230     aceRequest.dev_cap_list.items[0].param_list.count = 1;
231     aceRequest.dev_cap_list.items[0].param_list.items = new ace_param_t[1];
232     aceRequest.dev_cap_list.items[0].param_list.items[0].name =
233         const_cast<ace_string_t>(PARAM_URL);
234     aceRequest.dev_cap_list.items[0].param_list.items[0].value =
235         const_cast<ace_string_t>(url);
236
237     ace_bool_t result = ACE_FALSE;
238
239     LogDebug("Making ace check with new C-API");
240
241     ace_return_t ret = ace_check_access(&aceRequest, &result);
242
243     LogDebug("Result is: " << static_cast<int>(result));
244
245     delete[] aceRequest.dev_cap_list.items[0].param_list.items;
246     delete[] aceRequest.dev_cap_list.items;
247
248     return ACE_OK == ret && ACE_TRUE == result;
249 }
250 } // namespace (anonymous)
251
252 namespace InjectedBundleURIHandling {
253 bool processURI(const std::string& inputURI,
254                 const DPL::String& tizenId,
255                 WrtDB::WidgetSecurityModelVersion version)
256 {
257     if (version == WrtDB::WidgetSecurityModelVersion::WIDGET_SECURITY_MODEL_V1)
258     {
259         if (!checkWARP(inputURI.c_str(), tizenId))
260         {
261             LogWarning("Request was blocked by WARP: " << inputURI);
262
263             return false;
264         }
265     }
266
267 // disable for performance
268 #if 0
269     if (!preventSymlink(uri)) {
270         LogWarning("Request for symlink is invalid: " << uri);
271         return false;
272     }
273 #endif
274
275     return true;
276 }
277
278 bool processURI(const DPL::String& inputURI,
279                 const DPL::String& tizenId,
280                 WrtDB::WidgetSecurityModelVersion version)
281 {
282     DPL::Optional<DPL::String> optionalUri(inputURI);
283     if (optionalUri.IsNull()) {
284         LogDebug("uri is empty");
285         return true;
286     }
287
288     std::string uri = DPL::ToUTF8String(inputURI);
289     if (version ==
290         WrtDB::WidgetSecurityModelVersion::WIDGET_SECURITY_MODEL_V1)
291     {
292         if (!checkWARP(uri.c_str(), tizenId)) {
293             LogWarning("Request was blocked by WARP: " << uri);
294             return false;
295         }
296     }
297
298 // disable for performance
299 #if 0
300     if (!preventSymlink(uri)) {
301         LogWarning("Request for symlink is invalid: " << uri);
302         return false;
303     }
304 #endif
305
306     return true;
307 }
308
309 bool processMainResource(const DPL::String& inputURI,
310                          const DPL::String& tizenId,
311                          WrtDB::WidgetSecurityModelVersion version)
312 {
313     DPL::Optional<DPL::String> optionalUri(inputURI);
314     if (optionalUri.IsNull()) {
315         LogDebug("uri is empty");
316         return true;
317     }
318
319     std::string uri = DPL::ToUTF8String(inputURI);
320     if (version ==
321         WrtDB::WidgetSecurityModelVersion::WIDGET_SECURITY_MODEL_V1)
322     {
323         if (!checkWARP(uri.c_str(), tizenId)) {
324             LogWarning("Request was blocked by WARP: " << uri);
325             return false;
326         }
327     } else if (version ==
328         WrtDB::WidgetSecurityModelVersion::WIDGET_SECURITY_MODEL_V2)
329     {
330 #ifdef ALLOW_NAVIGATION_ENABLED
331         if (!checkAllowNavigation(uri.c_str(), tizenId)) {
332             LogWarning("Request was blocked by WARP: " << uri);
333             return false;
334         }
335 #else
336         return false;
337 #endif
338     }
339
340 // disable for performance
341 #if 0
342     if (!preventSymlink(uri)) {
343         LogWarning("Request for symlink is invalid: " << uri);
344         return false;
345     }
346 #endif
347
348     return true;
349 }
350
351 bool processURIForPlugin(const char* url)
352 {
353     if (!checkWhitelist(url)) {
354         return false;
355     }
356     return true;
357 }
358
359 std::string localizeURI(const std::string& inputURI, const std::string& tizenId)
360 {
361     if (inputURI.compare(0, strlen(SCHEME_TYPE_WIDGET), SCHEME_TYPE_WIDGET) &&
362         inputURI.compare(0, strlen(SCHEME_TYPE_FILE), SCHEME_TYPE_FILE) &&
363         inputURI.compare(0, strlen(SCHEME_TYPE_APP), SCHEME_TYPE_APP))
364     {
365         return inputURI;
366     }
367
368     std::string localizedURI = W3CFileLocalization::getFilePathInWidgetPackageFromUrl(tizenId, inputURI);
369
370     if (localizedURI.empty())
371     {
372         return inputURI;
373     }
374     else
375     {
376         return std::string("file://") + localizedURI;
377     }
378 }
379
380 DPL::OptionalString localizeURI(const DPL::String& inputURI,
381                                 const DPL::String& tizenId)
382 {
383     std::string uri = DPL::ToUTF8String(inputURI);
384     LogDebug("localizing url: " << uri);
385     auto urlcstr = uri.c_str();
386     const char *end = strstr(urlcstr, ":");
387     if (!end) {
388         LogDebug("no schema in link, return null");
389         // lack of schema
390         return DPL::Optional<DPL::String>::Null;
391     }
392     std::string scheme(urlcstr, end);
393
394 #ifdef APP_SCHEME_ENABLED
395     if (scheme != SCHEME_TYPE_WIDGET && scheme != SCHEME_TYPE_FILE && scheme != SCHEME_TYPE_APP) {
396 #else
397     if (scheme != SCHEME_TYPE_WIDGET && scheme != SCHEME_TYPE_FILE) {
398 #endif
399         LogDebug("scheme doesn't need to localize");
400         return DPL::OptionalString(inputURI);
401     }
402
403     DPL::Optional<DPL::String> found =
404         W3CFileLocalization::getFilePathInWidgetPackageFromUrl(
405             tizenId,
406             DPL::FromUTF8String(uri));
407
408     if (found.IsNull()) {
409         // In this case, path doesn't need to localize. return input uri
410         LogDebug("Path not found within current locale in current widget");
411
412         return DPL::OptionalString(inputURI);
413     } else {
414         DPL::String uri(L"file://" + *found);
415         LogDebug("Will load resource: " << uri);
416         LogDebug("finished");
417         return DPL::OptionalString(uri);
418     }
419 }
420 } // namespace InjectedBundleURIHandling