Implement Access control in ResourceMananger ( include CSP )
authorSeungkeun Lee <sngn.lee@samsung.com>
Thu, 28 May 2015 01:11:46 +0000 (10:11 +0900)
committerSeungkeun Lee <sngn.lee@samsung.com>
Mon, 1 Jun 2015 01:34:26 +0000 (10:34 +0900)
Change-Id: I08d8c9441180051181c07558e5b13f27966062ca

src/bundle/injected_bundle.cc
src/common/application_data.cc
src/common/application_data.h
src/common/resource_manager.cc
src/common/resource_manager.h
src/runtime/web_application.cc
src/runtime/web_application.h
src/runtime/web_view.cc
src/runtime/web_view.h
src/runtime/web_view_impl.cc
src/runtime/web_view_impl.h

index 4b618d8dcd453c416abbb757d3fd945ee12fe953..324a96439bcc30801fcc0819ab7a369164e85741 100755 (executable)
@@ -112,6 +112,12 @@ extern "C" void DynamicUrlParsing(
     *new_url = *old_url;
     return;
   }
+  // Check Access control
+  if (!res_manager->AllowedResource(*old_url)) {
+    // deined resource
+    *new_url = "about:blank";
+    return;
+  }
   *new_url = res_manager->GetLocalizedPath(*old_url);
 }
 
index 0a9ecc889b5ab37c2f99b40af1134bf75f613ebe..ddaafd34ec1dbff787fb8142f2e3552516244b8b 100755 (executable)
@@ -131,6 +131,21 @@ std::shared_ptr<const wgt::parse::ContentInfo>
   return content_info_;
 }
 
+std::shared_ptr<const wgt::parse::WarpInfo>
+    ApplicationData::warp_info() const {
+  return warp_info_;
+}
+
+std::shared_ptr<const wgt::parse::CSPInfo>
+    ApplicationData::csp_info() const {
+  return csp_info_;
+}
+
+std::shared_ptr<const wgt::parse::CSPInfo>
+    ApplicationData::csp_report_info() const {
+  return csp_report_info_;
+}
+
 
 bool ApplicationData::LoadManifestData() {
   std::string config_xml_path(application_path_ + kConfigXml);
@@ -150,7 +165,10 @@ bool ApplicationData::LoadManifestData() {
     SPLASH_SCREEN_HANDLER,
     TIZEN_APPLICATION_HANDLER,
     WIDGET_HANDLER,
-    CONTENT_HANDLER
+    CONTENT_HANDLER,
+    WARP_HANDLER,
+    CSP_HANDLER,
+    CSP_REPORT_HANDLER
   };
 
   std::vector<parser::ManifestHandler*> handlers = {
@@ -163,7 +181,13 @@ bool ApplicationData::LoadManifestData() {
     new wgt::parse::SplashScreenHandler,      // SPLASH_SCREEN_HANDLER
     new wgt::parse::TizenApplicationHandler,  // TIZEN_APPLICATION_HANDLER
     new wgt::parse::WidgetHandler,            // WIDGET_HANDLER
-    new wgt::parse::ContentHandler            // CONTENT_HANDLER
+    new wgt::parse::ContentHandler,           // CONTENT_HANDLER
+    new wgt::parse::WarpHandler,              // WARP_HANDLER
+    // CSP_HANDLER
+    new wgt::parse::CSPHandler(wgt::parse::CSPHandler::SecurityType::CSP),
+    // CSP_REPORT_HANDLER
+    new wgt::parse::CSPHandler(
+      wgt::parse::CSPHandler::SecurityType::CSP_REPORT_ONLY)
   };
 
   std::unique_ptr<parser::ManifestHandlerRegistry> registry;
@@ -229,6 +253,21 @@ bool ApplicationData::LoadManifestData() {
       manifest_parser.GetManifestData(
         handlers[ManifestHandlerType::CONTENT_HANDLER]->Key()));
 
+  warp_info_ =
+    std::static_pointer_cast<const wgt::parse::WarpInfo>(
+      manifest_parser.GetManifestData(
+        handlers[ManifestHandlerType::WARP_HANDLER]->Key()));
+
+  csp_info_ =
+    std::static_pointer_cast<const wgt::parse::CSPInfo>(
+      manifest_parser.GetManifestData(
+        handlers[ManifestHandlerType::CSP_HANDLER]->Key()));
+
+  csp_report_info_ =
+    std::static_pointer_cast<const wgt::parse::CSPInfo>(
+      manifest_parser.GetManifestData(
+        handlers[ManifestHandlerType::CSP_REPORT_HANDLER]->Key()));
+
   for (auto iter = handlers.begin(); iter != handlers.end(); ++iter) {
     delete *iter;
   }
index 9ef4ff4e3b3487093bffeaf8111c803320692578..8857cfbed26e225ae1a677b6ae70589436c8cfee 100755 (executable)
@@ -32,6 +32,8 @@
 #include <manifest_handlers/tizen_application_handler.h>
 #include <manifest_handlers/widget_handler.h>
 #include <manifest_handlers/content_handler.h>
+#include <manifest_handlers/warp_handler.h>
+#include <manifest_handlers/csp_handler.h>
 
 
 #include <memory>
@@ -66,6 +68,12 @@ class ApplicationData {
     widget_info() const;
   std::shared_ptr<const wgt::parse::ContentInfo>
     content_info() const;
+  std::shared_ptr<const wgt::parse::WarpInfo>
+    warp_info() const;
+  std::shared_ptr<const wgt::parse::CSPInfo>
+    csp_info() const;
+  std::shared_ptr<const wgt::parse::CSPInfo>
+    csp_report_info() const;
 
   const std::string application_path() const { return application_path_; }
   const std::string pkg_id() const { return pkg_id_; }
@@ -92,6 +100,12 @@ class ApplicationData {
     widget_info_;
   std::shared_ptr<const wgt::parse::ContentInfo>
     content_info_;
+  std::shared_ptr<const wgt::parse::WarpInfo>
+    warp_info_;
+  std::shared_ptr<const wgt::parse::CSPInfo>
+    csp_info_;
+  std::shared_ptr<const wgt::parse::CSPInfo>
+    csp_report_info_;
 
   std::string application_path_;
   std::string pkg_id_;
index f5fdcb5e4827e0a3a6fe367cb8be9e9fab54c4ac..7c36a31c20cee11995fe86038bf123d17418f7c3 100755 (executable)
@@ -41,9 +41,9 @@ typedef std::vector<AppControlInfo> AppControlList;
 // Scheme type
 const char* kSchemeTypeApp = "app://";
 const char* kSchemeTypeFile = "file://";
+const char* kSchemeTypeHttp = "http://";
+const char* kSchemeTypeHttps = "https://";
 // TODO(wy80.choi): comment out below unused const variables if needed.
-// const char* kSchemeTypeHttp = "http://";
-// const char* kSchemeTypeHttps = "https://";
 // const char* kSchemeTypeWidget = "widget://";
 
 // Default Start Files
@@ -166,6 +166,31 @@ static std::string InsertPrefixPath(const std::string& start_uri) {
   }
 }
 
+static void GetURLInfo(const std::string& url,
+                       std::string* scheme,
+                       std::string* domain,
+                       std::string* port) {
+  size_t end_of_scheme = url.find_first_of(':');
+  if (end_of_scheme == std::string::npos) {
+    end_of_scheme = -1;
+  } else {
+    *scheme = url.substr(0, end_of_scheme);
+  }
+
+  size_t start_of_domain = url.find_first_not_of('/', end_of_scheme+1);
+  size_t end_of_domain = url.find_first_of('/', start_of_domain);
+  *domain = url.substr(start_of_domain,
+      end_of_domain == std::string::npos ?
+          std::string::npos : end_of_domain - start_of_domain);
+  size_t port_separator = (*domain).find_first_of(':');
+  if (port_separator != std::string::npos) {
+    *port = (*domain).substr(port_separator+1);
+    *domain = (*domain).substr(0, port_separator);
+  } else {
+    *port = "80";
+  }
+}
+
 }  // namespace
 
 ResourceManager::Resource::Resource(const std::string& uri)
@@ -205,6 +230,13 @@ ResourceManager::ResourceManager(ApplicationData* application_data,
     : application_data_(application_data), locale_manager_(locale_manager) {
   if (application_data != NULL) {
     appid_ = application_data->tizen_application_info()->id();
+    if (application_data->csp_info() != NULL ||
+        application_data->csp_report_info() != NULL ||
+        application_data->allowed_navigation_info() != NULL) {
+      security_model_version_ = 2;
+    } else {
+      security_model_version_ = 1;
+    }
   }
 }
 
@@ -376,4 +408,160 @@ bool ResourceManager::Exists(const std::string& path) {
   return ret;
 }
 
+bool ResourceManager::AllowNavigation(const std::string& url) {
+  if (security_model_version_ == 2)
+    return CheckAllowNavigation(url);
+  return CheckWARP(url);
+}
+
+bool ResourceManager::AllowedResource(const std::string& url) {
+  if (security_model_version_ == 2)
+    return true;
+  return CheckWARP(url);
+}
+
+bool ResourceManager::CheckWARP(const std::string& url) {
+  // allow non-external resource
+  if (!utils::StartsWith(url, kSchemeTypeHttp) &&
+      !utils::StartsWith(url, kSchemeTypeHttps)) {
+    return true;
+  }
+
+  auto warp = application_data_->warp_info();
+  if (warp.get() == NULL)
+    return false;
+
+  auto find = warp_cache_.find(url);
+  if (find != warp_cache_.end()) {
+    return find->second;
+  }
+
+  bool& result = warp_cache_[url];
+  result = true;
+
+  // if didn't have a scheme, it means local resource
+  size_t end_of_scheme = url.find_first_of(':');
+  if (end_of_scheme == std::string::npos) {
+    return true;
+  }
+
+  std::string scheme;
+  std::string domain;
+  std::string port;
+  GetURLInfo(url, &scheme, &domain, &port);
+
+  for (auto& allow : warp->access_map()) {
+    if (allow.first == "*") {
+      return true;
+    }
+    std::string a_scheme, a_domain, a_port;
+    GetURLInfo(allow.first, &a_scheme, &a_domain, &a_port);
+
+    // should be match the scheme and port
+    if (a_scheme != scheme || a_port != port) {
+      continue;
+    }
+
+    // if domain alos was matched, allow resource
+    if (a_domain == domain) {
+      return true;
+    } else if (allow.second) {
+      // if does not match domain, should be check sub domain
+
+      // filter : test.com , subdomain=true
+      // url : aaa.test.com
+      // check url was ends with ".test.com"
+      a_domain = "." + a_domain;
+      if (utils::EndsWith(domain, a_domain)) {
+        return true;
+      }
+    }
+  }
+
+  return result = false;
+}
+
+bool ResourceManager::CheckAllowNavigation(const std::string& url) {
+  // allow non-external resource
+  if (!utils::StartsWith(url, kSchemeTypeHttp) &&
+      !utils::StartsWith(url, kSchemeTypeHttps)) {
+    return true;
+  }
+
+  auto allow = application_data_->allowed_navigation_info();
+  if (allow.get() == NULL)
+    return false;
+
+  auto find = warp_cache_.find(url);
+  if (find != warp_cache_.end()) {
+    return find->second;
+  }
+
+  bool& result = warp_cache_[url];
+  result = true;
+
+  // if didn't have a scheme, it means local resource
+  size_t end_of_scheme = url.find_first_of(':');
+  if (end_of_scheme == std::string::npos) {
+    return true;
+  }
+
+  std::string scheme;
+  std::string domain;
+  std::string port;
+  GetURLInfo(url, &scheme, &domain, &port);
+
+  for (auto& allow_domain : allow->GetAllowedDomains()) {
+    std::string a_scheme;
+    std::string a_domain;
+    std::string a_port;
+    GetURLInfo(allow_domain, &a_scheme, &a_domain, &a_port);
+
+    // check wildcard *
+    if (a_domain == "*") {
+      return true;
+    }
+
+    bool prefix_wild = false;
+    bool suffix_wild = false;
+    if (utils::StartsWith(a_domain, "*.")) {
+      prefix_wild = true;
+      // *.domain.com -> .domain.com
+      a_domain = a_domain.substr(1);
+    }
+    if (utils::EndsWith(a_domain, ".*")) {
+      suffix_wild = true;
+      // domain.* -> domain.
+      a_domain = a_domain.substr(0, a_domain.length() - 1);
+    }
+
+    if (!prefix_wild && !suffix_wild) {
+      // if no wildcard, should be exactly matched
+      if (domain == a_domain) {
+        return true;
+      }
+    } else if (prefix_wild && !suffix_wild) {
+      // *.domain.com : it shoud be "domain.com" or end with ".domain.com"
+      if (domain == a_domain.substr(1) ||
+          utils::EndsWith(domain, a_domain)) {
+        return true;
+      }
+    } else if (!prefix_wild && suffix_wild) {
+      // www.sample.* : it should be starts with "www.sample."
+      if (utils::StartsWith(domain, a_domain)) {
+        return true;
+      }
+    } else if (prefix_wild && suffix_wild) {
+      // *.sample.* : it should be starts with sample. or can find ".sample."
+      // in url
+      if (utils::StartsWith(domain, a_domain.substr(1)) ||
+          std::string::npos != domain.find(a_domain)) {
+        return true;
+      }
+    }
+  }
+
+  return result = false;
+}
+
 }  // namespace wrt
index c47e771964acfedb402d190715905bbaeded08e9..fed0c65d6b88dda326bbaa4d3a2f7f99813d73c2 100755 (executable)
@@ -73,6 +73,8 @@ class ResourceManager {
   // output : /[system path]/.../locales/.../
   std::string GetLocalizedPath(const std::string& origin);
   std::unique_ptr<Resource> GetStartResource(const AppControl* app_control);
+  bool AllowNavigation(const std::string& url);
+  bool AllowedResource(const std::string& url);
 
   void set_base_resource_path(const std::string& base_path);
 
@@ -81,14 +83,19 @@ class ResourceManager {
   std::string GetDefaultOrEmpty();
   // for localization
   bool Exists(const std::string& path);
+  bool CheckWARP(const std::string& url);
+  bool CheckAllowNavigation(const std::string& url);
 
   std::string resource_base_path_;
   std::string appid_;
   std::map<const std::string, bool> file_existed_cache_;
   std::map<const std::string, std::string> locale_cache_;
+  std::map<const std::string, bool> warp_cache_;
+
 
   ApplicationData* application_data_;
   LocaleManager* locale_manager_;
+  int security_model_version_;
 };
 
 }  // namespace wrt
index 030d0905a78d4da71b17f78b450c35286d7c7474..6556c919fbeb7cd235b346674a9da665809df5c5 100755 (executable)
@@ -104,12 +104,16 @@ const char* kRotationLockFeature = "rotation,lock";
 const char* kBackgroundMusicFeature = "background,music";
 const char* kSoundModeFeature = "sound,mode";
 const char* kBackgroundVibrationFeature = "background,vibration";
+const char* kCSPFeature = "csp";
+
 const char* kGeolocationPermissionPrefix = "__WRT_GEOPERM_";
 const char* kNotificationPermissionPrefix = "__WRT_NOTIPERM_";
 const char* kQuotaPermissionPrefix = "__WRT_QUOTAPERM_";
 const char* kCertificateAllowPrefix = "__WRT_CERTIPERM_";
 const char* kDBPrivateSection = "private";
 
+const char* kDefaultCSPRule =
+    "default-src *; script-src 'self'; style-src 'self'; object-src 'none';";
 
 bool FindPrivilege(wrt::ApplicationData* app_data,
                    const std::string& privilege) {
@@ -303,8 +307,26 @@ bool WebApplication::Initialize() {
   // TODO(sngn.lee): find the proxy url
   // ewk_context_proxy_uri_set(ewk_context_, ... );
 
-
-  // TODO(sngn.lee): check csp element in config.xml and enable - "csp"
+  if (app_data_->csp_info() != NULL ||
+      app_data_->csp_report_info() != NULL ||
+      app_data_->allowed_navigation_info() != NULL) {
+    security_model_version_ = 2;
+    if (app_data_->csp_info() == NULL ||
+        app_data_->csp_info()->security_rules().empty()) {
+      csp_rule_ = kDefaultCSPRule;
+    } else {
+      csp_rule_ = app_data_->csp_info()->security_rules();
+    }
+    if (app_data_->csp_report_info() != NULL &&
+        !app_data_->csp_report_info()->security_rules().empty()) {
+      csp_report_rule_ = app_data_->csp_report_info()->security_rules();
+    }
+    ewk_context_tizen_extensible_api_string_set(ewk_context_,
+                                                kCSPFeature,
+                                                EINA_TRUE);
+  } else {
+    security_model_version_ = 1;
+  }
 
   return true;
 }
@@ -644,15 +666,24 @@ void WebApplication::LaunchInspector(wrt::AppControl* appcontrol) {
 
 void WebApplication::SetupWebView(WebView* view) {
   view->SetEventListener(this);
+
+  // Setup CSP Rule
+  if (security_model_version_ == 2) {
+    view->SetCSPRule(csp_rule_, false);
+    if (!csp_report_rule_.empty()) {
+      view->SetCSPRule(csp_report_rule_, true);
+    }
+  }
+
   // TODO(sngn.lee): set UserAgent to WebView
-  // TODO(sngn.lee): set CSP
 }
 
 bool WebApplication::OnDidNavigation(WebView* /*view*/,
-                                     const std::string& /*url*/) {
+                                     const std::string& url) {
   // TODO(sngn.lee): scheme handling
   // except(file , http, https, app) pass to appcontrol and return false
-  return true;
+
+  return resource_manager_->AllowNavigation(url);
 }
 
 void WebApplication::OnNotificationPermissionRequest(
index 28d9178679f744c9b35fe52b691088292e1a0bc8..0729ba0adcb1f4dda73f0a76754e57cd85ff89f0 100755 (executable)
@@ -121,6 +121,9 @@ class WebApplication : public WebView::EventListener {
   std::unique_ptr<ResourceManager> resource_manager_;
   std::unique_ptr<wrt::AppControl> received_appcontrol_;
   std::function<void(void)> terminator_;
+  int security_model_version_;
+  std::string csp_rule_;
+  std::string csp_report_rule_;
 };
 
 }  // namespace wrt
index d76ef14989ddeb95f85c2fabffb1b6280b5aaf35..a203e680739a0965445ba7a00a6a9a22d809b240 100755 (executable)
@@ -78,4 +78,9 @@ void WebView::SetAppInfo(const std::string& app_name,
 bool WebView::SetUserAgent(const std::string& user_agent) {
   return impl_->SetUserAgent(user_agent.c_str());
 }
+
+void WebView::SetCSPRule(const std::string& rule, bool report_only) {
+  impl_->SetCSPRule(rule, report_only);
+}
+
 }  // namespace wrt
index 48831489e0bf92040dde28216db618bbe57daead..679c446b435988792ef816862ec7bc720e0a03fc 100755 (executable)
@@ -98,6 +98,7 @@ class WebView {
   bool EvalJavascript(const std::string& script);
   void SetAppInfo(const std::string& app_name, const std::string& version);
   bool SetUserAgent(const std::string& user_agent);
+  void SetCSPRule(const std::string& rule, bool report_only);
 
   void SetEventListener(EventListener* listener);
   Evas_Object* evas_object() const;
index 719eda8fa323a321e6fbe3280e820e917baf792f..510c0d2b637b4718cb075cb7c3d7d75b9a7255cd 100755 (executable)
@@ -765,4 +765,12 @@ bool WebViewImpl::SetUserAgent(const std::string& user_agent) {
   return ewk_view_user_agent_set(ewk_view_, user_agent.c_str());
 }
 
+void WebViewImpl::SetCSPRule(const std::string& rule, bool report_only) {
+  ewk_view_content_security_policy_set(
+      ewk_view_,
+      rule.c_str(),
+      report_only ? EWK_REPORT_ONLY : EWK_ENFORCE_POLICY);
+}
+
+
 }  // namespace wrt
index 00e85b572dad5fff4dd341cb6ce07f2cef682cfc..47801f57b8e1c85053b4bdf87a702342d0569c09 100755 (executable)
@@ -45,6 +45,7 @@ class WebViewImpl {
   bool EvalJavascript(const std::string& script);
   void SetAppInfo(const std::string& app_name, const std::string& version);
   bool SetUserAgent(const std::string& user_agent);
+  void SetCSPRule(const std::string& rule, bool report_only);
 
   void SetEventListener(WebView::EventListener* listener);
   Evas_Object* evas_object() const;