diff options
Diffstat (limited to 'special-mode-mgr')
-rw-r--r-- | special-mode-mgr/CMakeLists.txt | 4 | ||||
-rw-r--r-- | special-mode-mgr/cmake/FindPAM.cmake | 71 | ||||
-rw-r--r-- | special-mode-mgr/include/file.hpp | 79 | ||||
-rw-r--r-- | special-mode-mgr/src/specialmodemgr.cpp | 113 |
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) |