Replacement for realpath 18/325218/6
authorKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Wed, 4 Jun 2025 13:29:26 +0000 (15:29 +0200)
committerKrzysztof Jackiewicz <k.jackiewicz@samsung.com>
Thu, 5 Jun 2025 14:40:30 +0000 (14:40 +0000)
The realpath() function does not honor CAP_DAC_OVERRIDE nor
CAP_DAC_READ_SEARCH when traversing the directory tree. As a result a
process having these capabilities not running as root may get EACCES
when calling realpath(). This an undocumented/implementation specific
"feature" of realpath. The problem occurred after dropping root (while
keeping caps) in security-manager.

To work around it a following approach has been applied:
1. Open the file descriptor to the path in question.
2. Once it's opened, a proper symlink should appear in /proc/<pid>/fd
   pointing to the absolute canonicalized path.
3. Read the symlink target with readlink() to get that path.

Thanks to this replacement, the "system_share" group which was added
earlier to handle other realpath() related issue is no longer necessary
and it has been removed making security-manager rely on its
capabilities.

Change-Id: I98c8aa2668e098c04b472d57433617303ee4384e

src/common/service_impl_utils.cpp
src/server/main/server-main.cpp

index 2c64c8a646f95691411166b0d59f630a62373d40..30b24061586c59258d9ae87cecc95f9855aa437d 100644 (file)
@@ -27,6 +27,9 @@
 
 #include "service_impl_utils.h"
 
+#include <fcntl.h>
+#include <limits.h>
+
 #include <functional>
 
 #include <dpl/log/log.h>
@@ -104,13 +107,46 @@ std::string getSkelDir()
 
 std::string realPath(const std::string &path)
 {
-    auto real_pathPtr = makeUnique(realpath(path.c_str(), nullptr), free);
-    if (!real_pathPtr) {
-        WarnErrno("realpath(" << path << ')');
+    /*
+     * The realpath() function does not honor CAP_DAC_OVERRIDE nor CAP_DAC_READ_SEARCH when
+     * traversing the directory tree. As a result a process having these capabilities not running as
+     * root may get EACCES when calling realpath(). This an undocumented/implementation specific
+     * "feature" of realpath. The problem occurred after dropping root (while keeping caps) in
+     * security-manager.
+     *
+     * To work around it a following approach has been applied:
+     * 1. Open the file descriptor to the path in question.
+     * 2. Once it's opened, a proper symlink should appear in /proc/<pid>/fd pointing to the
+     *    absolute canonicalized path.
+     * 3. Read the symlink target with readlink() to get that path.
+     */
+    auto fd = open(path.c_str(), O_PATH | O_RDONLY);
+    if (fd == -1) {
+        WarnErrno("open(" << path << ')');
         return std::string();
     }
 
-    return real_pathPtr.get();
+    auto fdPtr = makeUnique(&fd, [](decltype(fd) *toClose) {
+        if (toClose != nullptr)
+            close(*toClose);
+    });
+
+    auto fdPath = std::string("/proc/self/fd/" + std::to_string(fd));
+    std::string buf(127, '\0');
+    ssize_t ret = 0;
+    for(;;) {
+        ret = readlink(fdPath.c_str(), buf.data(), buf.size());
+        if (ret < 0) {
+            WarnErrno("readlink(" << path << ')');
+            return std::string();
+        }
+        if (static_cast<size_t>(ret) < buf.size())
+            break;
+
+        buf.resize(buf.size() * 2 + 1);
+    }
+    buf.resize(ret);
+    return buf;
 }
 
 int getLegalPkgBaseDirs(const uid_t &uid,
index bb3ac743397827460c02327e5ba4e1a0f131d2b5..3c507428e812df82e2b0daa0f80a4d2a8291c59b 100644 (file)
@@ -120,11 +120,6 @@ void dropRoot()
     if (!caps)
         LogAndThrowErrno(SecurityManager::Exception, "Error in cap_get_proc");
 
-    // Set the system_share group to get +x in app directories
-    gid_t group = getGidByName("system_share");
-    if (setgroups(1, &group))
-        LogAndThrowErrno(SecurityManager::Exception, "Failed to set supplementary group " << group);
-
     // Change uid to non-root
     uid_t uid = getUidByName("security_fw");
     if (-1 == setuid(uid))