summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com>2020-01-30 19:41:20 +0300
committerRichard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com>2020-01-30 20:01:04 +0300
commit8f3c2240f6dd48888e8552e905945ffcd62900a6 (patch)
tree7c1a0a602371cfc576f91a89a476a5c92f4c3c1f
parentde66bb8fdc5fcd0df7f75af71155852d0182d5c8 (diff)
downloadprovingground-8f3c2240f6dd48888e8552e905945ffcd62900a6.tar.xz
Configure special user, if not set and in mfg mode
Configure special user with default passwod 0penBmc1, when the root user is not set with any password, and mark the password as expired, so that it will be forced to update on first login. This method can be used when Host interface is not available and we still need to enable root user. Note: This feature is under VALIDATION_UNSECURE flag, and hence will be available for reference only image. Tested: 1. Built the image without debug-tweaks and flashed the same 2. Confirmed that root user is not enabled during regular boot 3. Pressed power button for 15 seconds during AC Cycle, and entered manufacturing mode 4. Able to login to root user with password "0penBmc1" and updated the password to the new one, due to force password update 5. Verified upon rebooting, the newly configured password can be used to login to the serial console Change-Id: I53e68ebbe24110a116816a29fe1bf5b3142b8bc2 Signed-off-by: Richard Marian Thomaiyar <richard.marian.thomaiyar@linux.intel.com>
-rw-r--r--special-mode-mgr/CMakeLists.txt4
-rw-r--r--special-mode-mgr/cmake/FindPAM.cmake71
-rw-r--r--special-mode-mgr/include/file.hpp79
-rw-r--r--special-mode-mgr/src/specialmodemgr.cpp113
4 files changed, 267 insertions, 0 deletions
diff --git a/special-mode-mgr/CMakeLists.txt b/special-mode-mgr/CMakeLists.txt
index fa69da8..6fe7f86 100644
--- a/special-mode-mgr/CMakeLists.txt
+++ b/special-mode-mgr/CMakeLists.txt
@@ -1,5 +1,6 @@
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(specialmodemgr CXX)
+set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
@@ -38,12 +39,15 @@ link_directories(${DBUSINTERFACE_LIBRARY_DIRS})
find_package(PkgConfig REQUIRED)
pkg_check_modules(LOGGING phosphor-logging REQUIRED)
+find_package(PAM REQUIRED)
+
add_executable(${PROJECT_NAME} ${SRC_FILES})
target_link_libraries(${PROJECT_NAME} systemd)
target_link_libraries(${PROJECT_NAME} ${SDBUSPLUSPLUS_LIBRARIES})
target_link_libraries(${PROJECT_NAME} ${DBUSINTERFACE_LIBRARIES})
target_link_libraries(${PROJECT_NAME} ${Boost_LIBRARIES})
target_link_libraries(${PROJECT_NAME} phosphor_logging)
+target_link_libraries(${PROJECT_NAME} pam)
link_directories(${EXTERNAL_INSTALL_LOCATION}/lib)
diff --git a/special-mode-mgr/cmake/FindPAM.cmake b/special-mode-mgr/cmake/FindPAM.cmake
new file mode 100644
index 0000000..25307bd
--- /dev/null
+++ b/special-mode-mgr/cmake/FindPAM.cmake
@@ -0,0 +1,71 @@
+# - Try to find the PAM libraries
+# Once done this will define
+#
+# PAM_FOUND - system has pam
+# PAM_INCLUDE_DIR - the pam include directory
+# PAM_LIBRARIES - libpam library
+
+if (PAM_INCLUDE_DIR AND PAM_LIBRARY)
+ # Already in cache, be silent
+ set(PAM_FIND_QUIETLY TRUE)
+endif (PAM_INCLUDE_DIR AND PAM_LIBRARY)
+
+find_path(PAM_INCLUDE_DIR NAMES security/pam_appl.h pam/pam_appl.h)
+find_library(PAM_LIBRARY pam)
+find_library(DL_LIBRARY dl)
+
+if (PAM_INCLUDE_DIR AND PAM_LIBRARY)
+ set(PAM_FOUND TRUE)
+ if (DL_LIBRARY)
+ set(PAM_LIBRARIES ${PAM_LIBRARY} ${DL_LIBRARY})
+ else (DL_LIBRARY)
+ set(PAM_LIBRARIES ${PAM_LIBRARY})
+ endif (DL_LIBRARY)
+
+ if (EXISTS ${PAM_INCLUDE_DIR}/pam/pam_appl.h)
+ # darwin claims to be something special
+ set(HAVE_PAM_PAM_APPL_H 1)
+ endif (EXISTS ${PAM_INCLUDE_DIR}/pam/pam_appl.h)
+
+ if (NOT DEFINED PAM_MESSAGE_CONST)
+ include(CheckCXXSourceCompiles)
+ # XXX does this work with plain c?
+ check_cxx_source_compiles("
+#if ${HAVE_PAM_PAM_APPL_H}+0
+# include <pam/pam_appl.h>
+#else
+# include <security/pam_appl.h>
+#endif
+static int PAM_conv(
+ int num_msg,
+ const struct pam_message **msg, /* this is the culprit */
+ struct pam_response **resp,
+ void *ctx)
+{
+ return 0;
+}
+int main(void)
+{
+ struct pam_conv PAM_conversation = {
+ &PAM_conv, /* this bombs out if the above does not match */
+ 0
+ };
+ return 0;
+}
+" PAM_MESSAGE_CONST)
+ endif (NOT DEFINED PAM_MESSAGE_CONST)
+ set(PAM_MESSAGE_CONST ${PAM_MESSAGE_CONST} CACHE BOOL "PAM expects a conversation function with const pam_message")
+
+endif (PAM_INCLUDE_DIR AND PAM_LIBRARY)
+
+if (PAM_FOUND)
+ if (NOT PAM_FIND_QUIETLY)
+ message(STATUS "Found PAM: ${PAM_LIBRARIES}")
+ endif (NOT PAM_FIND_QUIETLY)
+else (PAM_FOUND)
+ if (PAM_FIND_REQUIRED)
+ message(FATAL_ERROR "PAM was not found")
+ endif(PAM_FIND_REQUIRED)
+endif (PAM_FOUND)
+
+mark_as_advanced(PAM_INCLUDE_DIR PAM_LIBRARY DL_LIBRARY PAM_MESSAGE_CONST)
diff --git a/special-mode-mgr/include/file.hpp b/special-mode-mgr/include/file.hpp
new file mode 100644
index 0000000..3ea0d50
--- /dev/null
+++ b/special-mode-mgr/include/file.hpp
@@ -0,0 +1,79 @@
+#pragma once
+
+#include <stdio.h>
+
+#include <filesystem>
+
+/** @class File
+ * @brief Responsible for handling file pointer
+ * Needed by putspent(3)
+ */
+class File
+{
+ private:
+ /** @brief handler for operating on file */
+ FILE* fp = NULL;
+
+ /** @brief File name. Needed in the case where the temp
+ * needs to be removed
+ */
+ const std::string& name;
+
+ /** @brief Should the file be removed at exit */
+ bool removeOnExit = false;
+
+ public:
+ File() = delete;
+ File(const File&) = delete;
+ File& operator=(const File&) = delete;
+ File(File&&) = delete;
+ File& operator=(File&&) = delete;
+
+ /** @brief Opens file and uses it to do file operation
+ *
+ * @param[in] name - File name
+ * @param[in] mode - File open mode
+ * @param[in] removeOnExit - File to be removed at exit or no
+ */
+ File(const std::string& filename, const std::string& mode,
+ bool removeExit = false) :
+ name(filename),
+ removeOnExit(removeExit)
+ {
+ fp = fopen(name.c_str(), mode.c_str());
+ }
+
+ /** @brief Opens file using provided file descriptor
+ *
+ * @param[in] fd - File descriptor
+ * @param[in] name - File name
+ * @param[in] mode - File open mode
+ * @param[in] removeOnExit - File to be removed at exit or no
+ */
+ File(int fd, const std::string& filename, const std::string& mode,
+ bool removeExit = false) :
+ name(filename),
+ removeOnExit(removeExit)
+ {
+ fp = fdopen(fd, mode.c_str());
+ }
+
+ ~File()
+ {
+ if (fp)
+ {
+ fclose(fp);
+ }
+
+ // Needed for exception safety
+ if (removeOnExit && std::filesystem::exists(name))
+ {
+ std::filesystem::remove(name);
+ }
+ }
+
+ auto operator()()
+ {
+ return fp;
+ }
+};
diff --git a/special-mode-mgr/src/specialmodemgr.cpp b/special-mode-mgr/src/specialmodemgr.cpp
index 132b26e..61c1d8a 100644
--- a/special-mode-mgr/src/specialmodemgr.cpp
+++ b/special-mode-mgr/src/specialmodemgr.cpp
@@ -15,9 +15,13 @@
*/
#include "specialmodemgr.hpp"
+#include "file.hpp"
+#include <security/pam_appl.h>
#include <sys/sysinfo.h>
+#include <pwd.h>
+#include <shadow.h>
#include <fstream>
#include <phosphor-logging/log.hpp>
#include <string>
@@ -46,6 +50,112 @@ using VariantValue =
namespace secCtrl = sdbusplus::xyz::openbmc_project::Control::Security::server;
+#ifdef BMC_VALIDATION_UNSECURE_FEATURE
+
+static int pamFunctionConversation(int numMsg, const struct pam_message** msg,
+ struct pam_response** resp, void* appdataPtr)
+{
+ if (appdataPtr == nullptr)
+ {
+ return PAM_AUTH_ERR;
+ }
+ size_t passSize = std::strlen(reinterpret_cast<char*>(appdataPtr)) + 1;
+ char* pass = reinterpret_cast<char*>(malloc(passSize));
+ std::strncpy(pass, reinterpret_cast<char*>(appdataPtr), passSize);
+
+ *resp = reinterpret_cast<pam_response*>(
+ calloc(numMsg, sizeof(struct pam_response)));
+
+ for (int i = 0; i < numMsg; ++i)
+ {
+ if (msg[i]->msg_style != PAM_PROMPT_ECHO_OFF)
+ {
+ continue;
+ }
+ resp[i]->resp = pass;
+ }
+ return PAM_SUCCESS;
+}
+
+int pamUpdatePasswd(const char* username, const char* password)
+{
+ const struct pam_conv localConversation = {pamFunctionConversation,
+ const_cast<char*>(password)};
+ pam_handle_t* localAuthHandle = NULL; // this gets set by pam_start
+
+ int retval =
+ pam_start("passwd", username, &localConversation, &localAuthHandle);
+
+ if (retval != PAM_SUCCESS)
+ {
+ return retval;
+ }
+
+ retval = pam_chauthtok(localAuthHandle, PAM_SILENT);
+ if (retval != PAM_SUCCESS)
+ {
+ pam_end(localAuthHandle, retval);
+ return retval;
+ }
+
+ return pam_end(localAuthHandle, PAM_SUCCESS);
+}
+
+static void checkAndConfigureSpecialUser()
+{
+ std::array<char, 4096> sbuffer{};
+ struct spwd spwd;
+ struct spwd* resultPtr = nullptr;
+ constexpr const char* specialUser = "root";
+ constexpr const char* specialUserDefPasswd = "0penBmc1";
+
+ // Query shadow entry for special user.
+ int status = getspnam_r(specialUser, &spwd, sbuffer.data(),
+ sbuffer.max_size(), &resultPtr);
+ if (status || (&spwd != resultPtr))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error in querying shadow entry for special user");
+ }
+ // Encrypted Password may be NULL or single character '!' if user is
+ // disabled
+ if (resultPtr->sp_pwdp[0] == 0 || resultPtr->sp_pwdp[1] == 0)
+ {
+ pamUpdatePasswd(specialUser, specialUserDefPasswd);
+ // requery the special user shadow entry as there is password
+ // update.
+ resultPtr = nullptr;
+ status = getspnam_r(specialUser, &spwd, sbuffer.data(),
+ sbuffer.max_size(), &resultPtr);
+ if (status || (&spwd != resultPtr))
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error in querying shadow entry for special user");
+ }
+ // Mark the password as expired to force update the password
+ File passwdFd("/etc/shadow", "r+");
+ if ((passwdFd)() == nullptr)
+ {
+ phosphor::logging::log<phosphor::logging::level::ERR>(
+ "Error in opening shadow file");
+ return;
+ }
+ // Mark the special user password as expired. This will
+ // force the user to set new password on first login.
+ resultPtr->sp_lstchg = 0;
+ putspent(resultPtr, (passwdFd)());
+ phosphor::logging::log<phosphor::logging::level::INFO>(
+ "Configured special user sucessfully");
+ }
+ else
+ {
+ phosphor::logging::log<phosphor::logging::level::DEBUG>(
+ "Skip configuring special user as it is already enabled");
+ }
+}
+
+#endif
+
SpecialModeMgr::SpecialModeMgr(
boost::asio::io_service& io_, sdbusplus::asio::object_server& srv_,
std::shared_ptr<sdbusplus::asio::connection>& conn_) :
@@ -192,6 +302,9 @@ void SpecialModeMgr::checkAndAddSpecialModeProperty(const std::string& provMode)
sd_journal_send("MESSAGE=%s", "Manufacturing mode - Entered",
"PRIORITY=%i", LOG_INFO, "REDFISH_MESSAGE_ID=%s",
"OpenBMC.0.1.ManufacturingModeEntered", NULL);
+#ifdef BMC_VALIDATION_UNSECURE_FEATURE
+ checkAndConfigureSpecialUser();
+#endif
}
addSpecialModeProperty();
if (!specialModeLockoutSeconds)