core: Fixing regression in PrivilegeHelper that prevented running as non-root
PrivilegeHelper was properly initialized only when general section was
present. When section was omitted, s_privilegedUid/s_privilegedGid/s_normalUid/s_normalGid
variables was not properly initialized (set to 0), resulting in
exception during the subsequent PrivilegeHelper::drop call.
Change-Id: Iffd7f05e7e6c86d227da2c9b7920ca6e3b607211
diff --git a/core/privilege-helper.cpp b/core/privilege-helper.cpp
new file mode 100644
index 0000000..6f87f43
--- /dev/null
+++ b/core/privilege-helper.cpp
@@ -0,0 +1,195 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2014 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
+ *
+ * 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 "privilege-helper.hpp"
+#include "core/logger.hpp"
+
+#include <pwd.h>
+#include <grp.h>
+
+namespace nfd {
+
+NFD_LOG_INIT("PrivilegeHelper");
+
+uid_t PrivilegeHelper::s_normalUid = ::geteuid();
+gid_t PrivilegeHelper::s_normalGid = ::getegid();
+
+uid_t PrivilegeHelper::s_privilegedUid = ::geteuid();
+gid_t PrivilegeHelper::s_privilegedGid = ::getegid();
+
+void
+PrivilegeHelper::initialize(const std::string& userName, const std::string& groupName)
+{
+ static const size_t MAX_GROUP_BUFFER_SIZE = 16384; // 16kB
+ 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 privilege helper with user \"" << userName << "\""
+ << " group \"" << groupName << "\"");
+
+ // workflow from man getpwnam_r
+
+ if (!groupName.empty())
+ {
+ static int groupSize = ::sysconf(_SC_GETGR_R_SIZE_MAX);
+
+ if (groupSize == -1)
+ {
+ groupSize = FALLBACK_GROUP_BUFFER_SIZE;
+ }
+
+ std::vector<char> groupBuffer(groupSize);
+ struct group group;
+ struct group* groupResult = 0;
+
+ int errorCode = getgrnam_r(groupName.c_str(), &group,
+ &groupBuffer[0], groupSize, &groupResult);
+
+ while (errorCode == ERANGE)
+ {
+ if (groupBuffer.size() * 2 > MAX_GROUP_BUFFER_SIZE)
+ {
+ throw PrivilegeHelper::Error("Cannot allocate large enough buffer for struct group");
+ }
+
+ groupBuffer.resize(groupBuffer.size() * 2);
+
+ errorCode = getgrnam_r(groupName.c_str(), &group,
+ &groupBuffer[0], groupBuffer.size(), &groupResult);
+ }
+
+ if (errorCode != 0 || !groupResult)
+ {
+ throw PrivilegeHelper::Error("Failed to get gid for \"" + groupName + "\"");
+ }
+
+ s_normalGid = group.gr_gid;
+ }
+
+ if (!userName.empty())
+ {
+ static int passwdSize = ::sysconf(_SC_GETPW_R_SIZE_MAX);
+
+ if (passwdSize == -1)
+ {
+ passwdSize = FALLBACK_PASSWD_BUFFER_SIZE;
+ }
+
+ std::vector<char> passwdBuffer(passwdSize);
+ struct passwd passwd;
+ struct passwd* passwdResult = 0;
+
+ int errorCode =
+ getpwnam_r(userName.c_str(), &passwd,
+ &passwdBuffer[0], passwdBuffer.size(), &passwdResult);
+
+ while (errorCode == ERANGE)
+ {
+ if (passwdBuffer.size() * 2 > MAX_PASSWD_BUFFER_SIZE)
+ {
+ throw PrivilegeHelper::Error("Cannot allocate large enough buffer for struct passwd");
+ }
+
+ passwdBuffer.resize(passwdBuffer.size() * 2);
+
+ errorCode = getpwnam_r(userName.c_str(), &passwd,
+ &passwdBuffer[0], passwdBuffer.size(), &passwdResult);
+ }
+
+ if (errorCode != 0 || !passwdResult)
+ {
+ throw PrivilegeHelper::Error("Failed to get uid for \"" + userName + "\"");
+ }
+
+ s_normalUid = passwd.pw_uid;
+ }
+}
+
+void
+PrivilegeHelper::drop()
+{
+ NFD_LOG_TRACE("dropping to effective gid=" << s_normalGid);
+ if (::setegid(s_normalGid) != 0)
+ {
+ std::stringstream error;
+ error << "Failed to drop to effective gid=" << s_normalGid;
+
+ throw PrivilegeHelper::Error(error.str());
+ }
+
+ NFD_LOG_TRACE("dropping to effective uid=" << s_normalUid);
+ if (::seteuid(s_normalUid) != 0)
+ {
+ std::stringstream error;
+ error << "Failed to drop to effective uid=" << s_normalUid;
+
+ throw PrivilegeHelper::Error(error.str());
+ }
+
+ NFD_LOG_INFO("dropped to effective uid=" << ::geteuid() << " gid=" << ::getegid());
+}
+
+void
+PrivilegeHelper::raise()
+{
+ NFD_LOG_TRACE("elevating to effective uid=" << s_privilegedUid);
+ if (::seteuid(s_privilegedUid) != 0)
+ {
+ std::stringstream error;
+ error << "Failed to elevate to effective uid=" << s_privilegedUid;
+
+ throw PrivilegeHelper::Error(error.str());
+ }
+
+ NFD_LOG_TRACE("elevating to effective gid=" << s_privilegedGid);
+ if (::setegid(s_privilegedGid) != 0)
+ {
+ std::stringstream error;
+ error << "Failed to elevate to effective gid=" << s_privilegedGid;
+
+ throw PrivilegeHelper::Error(error.str());
+ }
+ NFD_LOG_INFO("elevated to effective uid=" << ::geteuid() << " gid=" << ::getegid());
+}
+
+void
+PrivilegeHelper::runElevated(function<void()> f)
+{
+ raise();
+
+ try
+ {
+ f();
+ }
+ catch (...)
+ {
+ drop();
+ throw;
+ }
+ drop();
+}
+
+} // namespace nfd