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