Add parser plugin for checking executable file 47/259447/24
authorChanggyu Choi <changyu.choi@samsung.com>
Tue, 8 Jun 2021 06:19:55 +0000 (15:19 +0900)
committerChanggyu Choi <changyu.choi@samsung.com>
Fri, 23 Jul 2021 02:51:15 +0000 (11:51 +0900)
When a package is installed, the parser checks executable files.
If the package's type is "C/C++", it checks below condition.

   1) Check the file is whether same architecture with the platform.
   2) Check whether shared object or not.
   3) Check to have "main" symbol.
   4) Check that libraries with dependency on the file are linkable.

Change-Id: I4b17b13c15ab63e2cb6479ef78d05dda6512a11e
Signed-off-by: Changgyu Choi <changyu.choi@samsung.com>
packaging/aul.spec
parser/CMakeLists.txt
parser/exec-checker/CMakeLists.txt [new file with mode: 0644]
parser/exec-checker/data/exec_checker.info [new file with mode: 0644]
parser/exec-checker/inc/exec_checker.hh [new file with mode: 0644]
parser/exec-checker/inc/exec_checker_private.hh [new file with mode: 0644]
parser/exec-checker/inc/plugin_manager.hh [new file with mode: 0644]
parser/exec-checker/src/exec_checker.cc [new file with mode: 0644]
parser/exec-checker/src/exec_checker_plugin_interface.cc [new file with mode: 0644]
parser/exec-checker/src/plugin_manager.cc [new file with mode: 0644]

index b3294b2..65dbfde 100644 (file)
@@ -227,6 +227,8 @@ chmod +x %{_aulresdir}/tpk/install.sh
 %{TZ_SYS_DB}/.component.db*
 %{_sysconfdir}/package-manager/parserlib/metadata/*.so
 %{_sysconfdir}/package-manager/parserlib/libcomponent-based-application.so
+%{_sysconfdir}/package-manager/parserlib/libexec-checker.so*
+
 %attr(0755,root,root) %{_sysconfdir}/gumd/useradd.d/*
 %attr(0755,root,root) %{_datadir}/upgrade/scripts/501.component_upgrade.sh
 
index ea6a081..9cb57bf 100644 (file)
@@ -1,2 +1,3 @@
+ADD_SUBDIRECTORY(exec-checker)
 ADD_SUBDIRECTORY(component)
 ADD_SUBDIRECTORY(metadata)
diff --git a/parser/exec-checker/CMakeLists.txt b/parser/exec-checker/CMakeLists.txt
new file mode 100644 (file)
index 0000000..bb26f1b
--- /dev/null
@@ -0,0 +1,29 @@
+SET(TARGET_CHECK_EXEC_PLUGIN_PARSER "exec-checker")
+
+SET(SHARED_DIR "${CMAKE_INSTALL_PREFIX}/share")
+
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Wall -Werror -Winline")
+
+AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/src
+  CHECK_EXEC_PLUGIN_PARSER_SRCS)
+
+ADD_LIBRARY(${TARGET_CHECK_EXEC_PLUGIN_PARSER} SHARED ${CHECK_EXEC_PLUGIN_PARSER_SRCS})
+TARGET_LINK_LIBRARIES(${TARGET_CHECK_EXEC_PLUGIN_PARSER} PUBLIC dl)
+
+TARGET_INCLUDE_DIRECTORIES(${TARGET_CHECK_EXEC_PLUGIN_PARSER}
+  PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inc)
+
+APPLY_PKG_CONFIG(${TARGET_CHECK_EXEC_PLUGIN_PARSER} PUBLIC
+  DLOG_DEPS
+  GLIB_DEPS
+  LIBTZPLATFORM_CONFIG_DEPS
+  LIBXML_DEPS
+  PKGMGR_INFO_DEPS
+  PKGMGR_INSTALLER_DEPS
+  SQLITE3_DEPS
+)
+
+INSTALL(TARGETS ${TARGET_CHECK_EXEC_PLUGIN_PARSER}
+  DESTINATION ${SYSCONF_INSTALL_DIR}/package-manager/parserlib)
+INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/data/exec_checker.info
+  DESTINATION ${SHARED_DIR}/parser-plugins)
diff --git a/parser/exec-checker/data/exec_checker.info b/parser/exec-checker/data/exec_checker.info
new file mode 100644 (file)
index 0000000..9466ac2
--- /dev/null
@@ -0,0 +1,5 @@
+type="tag";name="ui-application";path="/etc/package-manager/parserlib/libexec-checker.so"
+type="tag";name="service-application";path="/etc/package-manager/parserlib/libexec-checker.so"
+type="tag";name="widget-application";path="/etc/package-manager/parserlib/libexec-checker.so"
+type="tag";name="watch-application";path="/etc/package-manager/parserlib/libexec-checker.so"
+type="tag";name="component-based-application";path="/etc/package-manager/parserlib/libexec-checker.so"
diff --git a/parser/exec-checker/inc/exec_checker.hh b/parser/exec-checker/inc/exec_checker.hh
new file mode 100644 (file)
index 0000000..4324d29
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef EXEC_CHECKER_HH_
+#define EXEC_CHECKER_HH_
+
+#include <string>
+
+namespace plugin {
+
+class ExecChecker {
+ public:
+  explicit ExecChecker(std::string path);
+  bool IsShared();
+  bool CheckDependencyLibs();
+  bool IsSameArch(const ExecChecker& exe);
+
+ private:
+  std::string path_;
+  int class_bit_;
+  uint16_t arch_bit_;
+};
+
+}  // namespace plugin
+
+#endif  // EXEC_CHECKER_HH_
diff --git a/parser/exec-checker/inc/exec_checker_private.hh b/parser/exec-checker/inc/exec_checker_private.hh
new file mode 100644 (file)
index 0000000..6f0916f
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef EXEC_CEHCKER_PRIVATE_HH_
+#define EXEC_CEHCKER_PRIVATE_HH_
+
+#include <dlog.h>
+#include <stdio.h>
+
+#undef API
+#define API __attribute__((visibility("default")))
+
+#define ROOT_UID 0
+
+#undef LOG_TAG
+#define LOG_TAG "EXEC_CHECKER_PLUGIN_PARSER"
+
+#define LOGE_STD(fmt, arg...) \
+    fprintf(stderr, "[%s][ERROR] " fmt "\n", LOG_TAG, ##arg)
+#define LOGW_STD(fmt, arg...) \
+    fprintf(stderr, "[%s][WARNING] " fmt "\n", LOG_TAG, ##arg)
+#define LOGI_STD(fmt, arg...) printf("[%s][INFO] " fmt "\n", LOG_TAG, ##arg)
+
+#define GLOBAL_USER tzplatform_getuid(TZ_SYS_GLOBALAPP_USER)
+
+#endif  // EXEC_CEHCKER_PRIVATE_HH_
diff --git a/parser/exec-checker/inc/plugin_manager.hh b/parser/exec-checker/inc/plugin_manager.hh
new file mode 100644 (file)
index 0000000..a322a65
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef EXEC_CHECKER_PLUGIN_MANAGER_HH_
+#define EXEC_CHECKER_PLUGIN_MANAGER_HH_
+
+#include <libxml/tree.h>
+#include <memory>
+#include <string>
+
+#include "exec_checker.hh"
+
+namespace plugin {
+
+class PluginManager {
+ public:
+  static PluginManager& GetInst();
+  void Init(xmlDocPtr doc, std::string package);
+  int Process();
+  ~PluginManager() = default;
+
+ private:
+  PluginManager();
+
+ private:
+  std::unique_ptr<ExecChecker> self_;
+  std::string package_;
+  xmlDocPtr doc_;
+};
+
+}  // namespace plugin
+
+#endif  // EXEC_CHECKER_PLUGIN_MANAGER_HH_
diff --git a/parser/exec-checker/src/exec_checker.cc b/parser/exec-checker/src/exec_checker.cc
new file mode 100644 (file)
index 0000000..713665a
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <dlfcn.h>
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <fstream>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "exec_checker.hh"
+#include "exec_checker_private.hh"
+
+namespace plugin {
+namespace {
+
+union header {
+  char c[4096];
+  Elf32_Ehdr ehdr32;
+  Elf64_Ehdr ehdr64;
+};
+
+}  // namespace
+
+ExecChecker::ExecChecker(std::string path) : path_(std::move(path)) {
+  int fd = open(path_.c_str(), O_RDONLY);
+  if (fd < 0) {
+    LOGE_STD("Failed to open file(%s). errno(%d)", path_.c_str(), errno);
+    return;
+  }
+
+  header h;
+  ssize_t nbyte = read(fd, h.c, sizeof(h));
+  close(fd);
+  if (nbyte < 2) {
+    LOGE_STD("Failed to read file(%s). errno(%d)", path_.c_str(), errno);
+    return;
+  }
+
+  if (nbyte < SELFMAG || memcmp(h.c, ELFMAG, SELFMAG) != 0) {
+    LOGE_STD("This binary is not elf format(%s)", path_.c_str());
+    return;
+  }
+
+  if (nbyte >= static_cast<int>(sizeof(Elf32_Ehdr)) &&
+      h.c[EI_CLASS] == ELFCLASS32) {
+    class_bit_ = 32;
+    arch_bit_ = h.ehdr32.e_machine;
+  } else if (nbyte >= static_cast<int>(sizeof(Elf64_Ehdr)) &&
+      h.c[EI_CLASS] == ELFCLASS64) {
+    class_bit_ = 64;
+    arch_bit_ = h.ehdr64.e_machine;
+  }
+  LOGI_STD("class: %dbit, e_machine: %hu", class_bit_, arch_bit_);
+}
+
+bool ExecChecker::IsShared() {
+  void* handle = dlopen(path_.c_str(), RTLD_LAZY | RTLD_GLOBAL);
+  if (handle == nullptr) {
+    LOGE_STD("(%s) is not shared object.", path_.c_str());
+    return false;
+  }
+
+  auto* main = dlsym(handle, "main");
+  if (main == nullptr)
+    LOGW_STD("Not found main symbol(%s). err(%s)", path_.c_str(), dlerror());
+
+  dlclose(handle);
+  return main != nullptr;
+}
+
+bool ExecChecker::CheckDependencyLibs() {
+  std::string root_path = path_.substr(0, path_.find_last_of('/') - 4);
+  char tmp_format[] = "/tmp/XXXXXX";
+  auto* tmp = tmpnam_r(tmp_format);
+  if (tmp == nullptr) {
+    LOGE_STD("Failed to make temp file. errno(%d)", errno);
+    return false;
+  }
+
+  std::string cmd = "LD_LIBRARY_PATH=" + root_path + "/lib /usr/bin/ldd " +
+                    path_ + " > " + tmp;
+  bool pass = true;
+  int ret = WEXITSTATUS(system(cmd.c_str()));
+  if (ret != 0) {
+    LOGE_STD("Failed to execute ldd %s, (%d)", cmd.c_str(), ret);
+    return false;
+  }
+
+  std::ifstream f(tmp);
+  char buf[1000];
+  while (!f.eof()) {
+    f.getline(buf, sizeof(buf));
+    if (buf == nullptr)
+      continue;
+
+    std::string line = buf;
+    if (line.find("not found", 0) != line.npos) {
+      LOGE_STD("Not found library (%s)",
+          line.substr(0, line.find_first_of(' ', 0)).c_str());
+      pass = false;
+      break;
+    }
+  }
+
+  f.close();
+  remove(tmp);
+  return pass;
+}
+
+bool ExecChecker::IsSameArch(const ExecChecker& exe) {
+  return class_bit_ == exe.class_bit_ && arch_bit_ == exe.arch_bit_;
+}
+
+}  // namespace plugin
diff --git a/parser/exec-checker/src/exec_checker_plugin_interface.cc b/parser/exec-checker/src/exec_checker_plugin_interface.cc
new file mode 100644 (file)
index 0000000..9b2da82
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glib.h>
+#include <libxml/tree.h>
+
+#include "exec_checker_private.hh"
+#include "plugin_manager.hh"
+
+namespace {
+
+int Run(xmlDocPtr doc, const char* package) {
+  if (doc == nullptr || package == nullptr) {
+    LOGE_STD("Invalid parameter doc(%p) package(%p)", doc, package);
+    return -1;
+  }
+  auto& manager = plugin::PluginManager::GetInst();
+  manager.Init(doc, package);
+  return manager.Process();
+}
+
+}  // namespace
+
+extern "C" API int PKGMGR_PARSER_PLUGIN_INSTALL(xmlDocPtr doc,
+    const char* package) {
+  return Run(doc, package);
+}
+
+extern "C" API int PKGMGR_PARSER_PLUGIN_UPGRADE(xmlDocPtr doc,
+    const char* package) {
+  return Run(doc, package);
+}
+
+extern "C" API int PKGMGR_PARSER_PLUGIN_RECOVERINSTALL(xmlDocPtr doc,
+    const char* package) {
+  return Run(doc, package);
+}
+
+extern "C" API int PKGMGR_PARSER_PLUGIN_RECOVERUPGRADE(xmlDocPtr doc,
+    const char* package) {
+  return Run(doc, package);
+}
diff --git a/parser/exec-checker/src/plugin_manager.cc b/parser/exec-checker/src/plugin_manager.cc
new file mode 100644 (file)
index 0000000..91e1bdc
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <libxml/tree.h>
+#include <pkgmgr-info.h>
+#include <pkgmgr_installer_info.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "exec_checker.hh"
+#include "exec_checker_private.hh"
+#include "plugin_manager.hh"
+
+namespace plugin {
+namespace {
+
+std::string GetAttribute(xmlNode* node, const char* key) {
+  std::string ret;
+  xmlChar* val = xmlGetProp(node, reinterpret_cast<const xmlChar*>(key));
+  if (val != nullptr) {
+    ret = reinterpret_cast<char*>(val);
+    xmlFree(val);
+  }
+
+  return ret;
+}
+
+}  // namespace
+
+PluginManager::PluginManager() {
+  char path[1024];
+  int nbyte = readlink("/proc/self/exe", path, sizeof(path) - 1);
+  if (nbyte == -1) {
+    LOGE_STD("Failed to get path. errno(%d)", errno);
+    return;
+  }
+
+  path[nbyte] = '\0';
+  self_ = std::unique_ptr<ExecChecker>(new (std::nothrow) ExecChecker(path));
+  if (self_ == nullptr)
+    LOGE_STD("Out of memory");
+}
+
+PluginManager& PluginManager::GetInst() {
+  static PluginManager inst;
+  return inst;
+}
+
+void PluginManager::Init(xmlDocPtr doc, std::string package) {
+  package_ = std::move(package);
+  doc_ = doc;
+}
+
+int PluginManager::Process() {
+  xmlNode* root = xmlDocGetRootElement(doc_);
+  if (root == nullptr) {
+    LOGE_STD("Failed to get root element");
+    return -1;
+  }
+  uid_t uid;
+  int ret = pkgmgr_installer_info_get_target_uid(&uid);
+  if (ret != PMINFO_R_OK) {
+    LOGE_STD("Failed to get target uid. (%s)", package_.c_str());
+    return -1;
+  }
+
+  for (xmlNode* node = root->children; node; node = node->next) {
+    auto* apptype = reinterpret_cast<const char*>(node->name);
+    if (apptype == nullptr)
+      continue;
+
+    std::string appid = GetAttribute(node, "appid");
+    if (appid.empty()) {
+      LOGE_STD("Failed to find appid. (%s)", appid.c_str());
+      return -1;
+    }
+
+    pkgmgrinfo_appinfo_h app_info;
+    ret = pkgmgrinfo_appinfo_get_usr_appinfo(appid.c_str(), uid, &app_info);
+    if (ret < 0) {
+      LOGE_STD("Failed to get appinfo. (%s)", appid.c_str());
+      return -1;
+    }
+    auto app_info_auto =
+        std::unique_ptr<std::remove_pointer_t<pkgmgrinfo_appinfo_h>,
+                        decltype(pkgmgrinfo_appinfo_destroy_appinfo)*>(
+            app_info, pkgmgrinfo_appinfo_destroy_appinfo);
+
+    char* exec = nullptr;
+    ret = pkgmgrinfo_appinfo_get_exec(app_info, &exec);
+    if (ret < 0) {
+      LOGE_STD("Failed to get app path. (%s)", package_.c_str());
+      return -1;
+    }
+
+    std::string app_type = GetAttribute(node, "type");
+    if (app_type != "capp" && app_type != "c++app")
+      continue;
+
+    LOGI_STD("Check binary: %s", exec);
+    ExecChecker checker(exec);
+
+    if (!self_->IsSameArch(checker))
+      return -1;
+    checker.IsShared();
+    if (!checker.CheckDependencyLibs())
+      return -1;
+  }
+
+  return 0;
+}
+
+}  // namespace plugin