core: move nfd-specific files to daemon/

Refs: #4922
Change-Id: I2243dbb87c63f9cbaf7d7051d7a0d4bca2f9fdb5
diff --git a/daemon/common/privilege-helper.cpp b/daemon/common/privilege-helper.cpp
new file mode 100644
index 0000000..7cdfcd0
--- /dev/null
+++ b/daemon/common/privilege-helper.cpp
@@ -0,0 +1,163 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/*
+ * Copyright (c) 2014-2019,  Regents of the University of California,
+ *                           Arizona Board of Regents,
+ *                           Colorado State University,
+ *                           University Pierre & Marie Curie, Sorbonne University,
+ *                           Washington University in St. Louis,
+ *                           Beijing Institute of Technology,
+ *                           The University of Memphis.
+ *
+ * This file is part of NFD (Named Data Networking Forwarding Daemon).
+ * See AUTHORS.md for complete list of NFD authors and contributors.
+ *
+ * NFD is free software: you can redistribute it and/or modify it under the terms
+ * of the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
+ * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.  See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * NFD, e.g., in COPYING.md file.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "common/privilege-helper.hpp"
+#include "common/logger.hpp"
+
+#include <pwd.h>
+#include <grp.h>
+
+namespace nfd {
+
+NFD_LOG_INIT(PrivilegeHelper);
+
+#ifdef HAVE_PRIVILEGE_DROP_AND_ELEVATE
+uid_t PrivilegeHelper::s_normalUid = ::geteuid();
+gid_t PrivilegeHelper::s_normalGid = ::getegid();
+
+uid_t PrivilegeHelper::s_privilegedUid = ::geteuid();
+gid_t PrivilegeHelper::s_privilegedGid = ::getegid();
+#endif // HAVE_PRIVILEGE_DROP_AND_ELEVATE
+
+void
+PrivilegeHelper::initialize(const std::string& userName, const std::string& groupName)
+{
+#ifdef HAVE_PRIVILEGE_DROP_AND_ELEVATE
+  static const size_t MAX_GROUP_BUFFER_SIZE = 16384; // 16 KiB
+  static const size_t MAX_PASSWD_BUFFER_SIZE = 16384;
+
+  static const size_t FALLBACK_GROUP_BUFFER_SIZE = 1024;
+  static const size_t FALLBACK_PASSWD_BUFFER_SIZE = 1024;
+
+  NFD_LOG_TRACE("initializing with user \"" << userName << "\""
+                << " group \"" << groupName << "\"");
+
+  // workflow from man getpwnam_r
+
+  if (!groupName.empty()) {
+    static long groupSize = ::sysconf(_SC_GETGR_R_SIZE_MAX);
+
+    if (groupSize == -1)
+      groupSize = FALLBACK_GROUP_BUFFER_SIZE;
+
+    std::vector<char> groupBuffer(static_cast<size_t>(groupSize));
+    struct group group;
+    struct group* groupResult = nullptr;
+
+    int errorCode = getgrnam_r(groupName.data(), &group,
+                               groupBuffer.data(), groupBuffer.size(), &groupResult);
+
+    while (errorCode == ERANGE) {
+      if (groupBuffer.size() * 2 > MAX_GROUP_BUFFER_SIZE)
+        throw Error("Cannot allocate large enough buffer for struct group");
+
+      groupBuffer.resize(groupBuffer.size() * 2);
+      errorCode = getgrnam_r(groupName.data(), &group,
+                             groupBuffer.data(), groupBuffer.size(), &groupResult);
+    }
+
+    if (errorCode != 0 || !groupResult)
+      throw Error("Failed to get gid for \"" + groupName + "\"");
+
+    s_normalGid = group.gr_gid;
+  }
+
+  if (!userName.empty()) {
+    static long passwdSize = ::sysconf(_SC_GETPW_R_SIZE_MAX);
+
+    if (passwdSize == -1)
+      passwdSize = FALLBACK_PASSWD_BUFFER_SIZE;
+
+    std::vector<char> passwdBuffer(static_cast<size_t>(passwdSize));
+    struct passwd passwd;
+    struct passwd* passwdResult = nullptr;
+
+    int errorCode = getpwnam_r(userName.data(), &passwd,
+                               passwdBuffer.data(), passwdBuffer.size(), &passwdResult);
+
+    while (errorCode == ERANGE) {
+      if (passwdBuffer.size() * 2 > MAX_PASSWD_BUFFER_SIZE)
+        throw Error("Cannot allocate large enough buffer for struct passwd");
+
+      passwdBuffer.resize(passwdBuffer.size() * 2);
+      errorCode = getpwnam_r(userName.data(), &passwd,
+                             passwdBuffer.data(), passwdBuffer.size(), &passwdResult);
+    }
+
+    if (errorCode != 0 || !passwdResult)
+      throw Error("Failed to get uid for \"" + userName + "\"");
+
+    s_normalUid = passwd.pw_uid;
+  }
+#else
+  if (!userName.empty() || !groupName.empty()) {
+    throw Error("Dropping and raising privileges is not supported on this platform");
+  }
+#endif // HAVE_PRIVILEGE_DROP_AND_ELEVATE
+}
+
+void
+PrivilegeHelper::drop()
+{
+#ifdef HAVE_PRIVILEGE_DROP_AND_ELEVATE
+  if (::geteuid() == s_normalUid && ::getegid() == s_normalGid)
+    return;
+
+  NFD_LOG_TRACE("dropping to effective gid=" << s_normalGid);
+  if (::setegid(s_normalGid) != 0)
+    throw Error("Failed to drop to effective gid=" + to_string(s_normalGid));
+
+  NFD_LOG_TRACE("dropping to effective uid=" << s_normalUid);
+  if (::seteuid(s_normalUid) != 0)
+    throw Error("Failed to drop to effective uid=" + to_string(s_normalUid));
+
+  NFD_LOG_INFO("dropped to effective uid=" << ::geteuid() << " gid=" << ::getegid());
+#else
+  NFD_LOG_WARN("Dropping privileges is not supported on this platform");
+#endif // HAVE_PRIVILEGE_DROP_AND_ELEVATE
+}
+
+void
+PrivilegeHelper::raise()
+{
+#ifdef HAVE_PRIVILEGE_DROP_AND_ELEVATE
+  if (::geteuid() == s_privilegedUid && ::getegid() == s_privilegedGid)
+    return;
+
+  NFD_LOG_TRACE("elevating to effective uid=" << s_privilegedUid);
+  if (::seteuid(s_privilegedUid) != 0)
+    throw Error("Failed to elevate to effective uid=" + to_string(s_privilegedUid));
+
+  NFD_LOG_TRACE("elevating to effective gid=" << s_privilegedGid);
+  if (::setegid(s_privilegedGid) != 0)
+    throw Error("Failed to elevate to effective gid=" + to_string(s_privilegedGid));
+
+  NFD_LOG_INFO("elevated to effective uid=" << ::geteuid() << " gid=" << ::getegid());
+#else
+  NFD_LOG_WARN("Elevating privileges is not supported on this platform");
+#endif // HAVE_PRIVILEGE_DROP_AND_ELEVATE
+}
+
+} // namespace nfd