summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am16
-rw-r--r--configure.ac1
-rw-r--r--user_channel/passwd_mgr.cpp261
-rw-r--r--user_channel/passwd_mgr.hpp86
-rw-r--r--user_channel/shadowlock.hpp50
-rw-r--r--user_channel/user_layer.cpp32
-rw-r--r--user_channel/user_layer.hpp33
7 files changed, 478 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am
index a5dfdab..7be3bc9 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -39,6 +39,7 @@ ipmid_LDFLAGS = \
$(LIBADD_DLOPEN) \
$(PHOSPHOR_LOGGING_LIBS) \
$(PHOSPHOR_DBUS_INTERFACES_LIBS) \
+ $(CRYPTO_LIBS) \
-lstdc++fs \
-pthread \
-export-dynamic
@@ -61,8 +62,20 @@ fru-read-gen.cpp:
channel-gen.cpp:
$(AM_V_GEN)@CHANNELGEN@ -o $(top_builddir) generate-cpp
+libuserlayerdir = ${libdir}
+libuserlayer_LTLIBRARIES = libuserlayer.la
+libuserlayer_la_SOURCES = \
+ user_channel/user_layer.cpp \
+ user_channel/passwd_mgr.cpp
+
+libuserlayer_la_LDFLAGS = $(SYSTEMD_LIBS) $(libmapper_LIBS) \
+ $(PHOSPHOR_LOGGING_LIBS) $(PHOSPHOR_DBUS_INTERFACES_LIBS) -lstdc++fs \
+ $(LIBPAM) $(LIBCRYPT) -lpam_misc -lssl -version-info 0:0:0 -shared
+libuserlayer_la_CXXFLAGS = $(SYSTEMD_CFLAGS) $(libmapper_CFLAGS) \
+ $(PHOSPHOR_LOGGING_CFLAGS) $(PHOSPHOR_DBUS_INTERFACES_CFLAGS)
libipmi20dir = ${libdir}/ipmid-providers
libipmi20_LTLIBRARIES = libipmi20.la
+libipmi20_la_DEPENDENCIES = libuserlayer.la
libipmi20_la_SOURCES = \
net.cpp \
app/channel.cpp \
@@ -95,7 +108,7 @@ TESTS = $(check_PROGRAMS)
libipmi20_la_LDFLAGS = $(SYSTEMD_LIBS) $(libmapper_LIBS) \
$(PHOSPHOR_LOGGING_LIBS) $(PHOSPHOR_DBUS_INTERFACES_LIBS) -lstdc++fs \
- -version-info 0:0:0 -shared
+ -luserlayer -version-info 0:0:0 -shared
libipmi20_la_CXXFLAGS = $(SYSTEMD_CFLAGS) $(libmapper_CFLAGS) \
$(BOOST_CXX) $(PHOSPHOR_LOGGING_CFLAGS) \
$(PHOSPHOR_DBUS_INTERFACES_CFLAGS) \
@@ -122,6 +135,7 @@ libsysintfcmds_la_CXXFLAGS = $(SYSTEMD_CFLAGS) \
nobase_include_HEADERS = \
host-ipmid/iana.hpp \
+ user_channel/user_layer.hpp \
host-ipmid/ipmid-api.h \
host-ipmid/ipmid-host-cmd.hpp \
host-ipmid/ipmid-host-cmd-utils.hpp \
diff --git a/configure.ac b/configure.ac
index 5cbdfc6..78af3df 100644
--- a/configure.ac
+++ b/configure.ac
@@ -25,6 +25,7 @@ AS_IF([test "x$enable_softoff" != "xno"],
# Checks for libraries.
AC_CHECK_LIB([mapper], [mapper_get_service], ,[AC_MSG_ERROR([Could not find libmapper...openbmc/phosphor-objmgr package required])])
PKG_CHECK_MODULES([SYSTEMD], [libsystemd >= 221], [], [AC_MSG_ERROR(["systemd required and not found"])])
+PKG_CHECK_MODULES([CRYPTO], [libcrypto >= 1.0.2g], ,[AC_MSG_ERROR([can't find openssl libcrypto])])
PKG_CHECK_MODULES([PHOSPHOR_LOGGING], [phosphor-logging],, [AC_MSG_ERROR([Could not find phosphor-logging...openbmc/phosphor-logging package required])])
PKG_CHECK_MODULES([PHOSPHOR_DBUS_INTERFACES], [phosphor-dbus-interfaces],, [AC_MSG_ERROR([Could not find phosphor-dbus-interfaces...openbmc/phosphor-dbus-interfaces package required])])
PKG_CHECK_MODULES([SDBUSPLUS], [sdbusplus],,\
diff --git a/user_channel/passwd_mgr.cpp b/user_channel/passwd_mgr.cpp
new file mode 100644
index 0000000..1d8a1ce
--- /dev/null
+++ b/user_channel/passwd_mgr.cpp
@@ -0,0 +1,261 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include "passwd_mgr.hpp"
+
+#include "shadowlock.hpp"
+
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <cstring>
+#include <fstream>
+#include <phosphor-logging/log.hpp>
+
+namespace ipmi
+{
+
+static const char* passwdFileName = "/etc/ipmi_pass";
+static const char* encryptKeyFileName = "/etc/key_file";
+static const size_t maxKeySize = 8;
+
+static const char* META_PASSWD_SIG = "=OPENBMC=";
+
+/*
+ * Meta data struct for encrypted password file
+ */
+struct metaPassStruct
+{
+ char signature[10];
+ unsigned char reseved[2];
+ size_t hashSize;
+ size_t ivSize;
+ size_t dataSize;
+ size_t padSize;
+ size_t macSize;
+};
+
+using namespace phosphor::logging;
+
+PasswdMgr::PasswdMgr()
+{
+ initPasswordMap();
+}
+
+std::string PasswdMgr::getPasswdByUserName(const std::string& userName)
+{
+ checkAndReload();
+ auto iter = passwdMapList.find(userName);
+ if (iter == passwdMapList.end())
+ {
+ return std::string();
+ }
+ return iter->second;
+}
+
+void PasswdMgr::checkAndReload(void)
+{
+ struct stat fileStat = {};
+ if (stat(passwdFileName, &fileStat) != 0)
+ {
+ log<level::DEBUG>("Error in getting last updated time stamp");
+ return;
+ }
+ std::time_t updatedTime = fileStat.st_mtime;
+ if (fileLastUpdatedTime != updatedTime)
+ {
+ log<level::DEBUG>("Reloading password map list");
+ passwdMapList.clear();
+ initPasswordMap();
+ }
+}
+
+int PasswdMgr::decrypt(const EVP_CIPHER* cipher, uint8_t* key, size_t keyLen,
+ uint8_t* iv, size_t ivLen, uint8_t* inBytes,
+ size_t inBytesLen, uint8_t* mac, size_t macLen,
+ uint8_t* outBytes, size_t* outBytesLen)
+{
+
+ if (cipher == NULL || key == NULL || iv == NULL || inBytes == NULL ||
+ outBytes == NULL || mac == NULL || inBytesLen == 0 ||
+ (size_t)EVP_CIPHER_key_length(cipher) > keyLen ||
+ (size_t)EVP_CIPHER_iv_length(cipher) > ivLen)
+ {
+ log<level::DEBUG>("Error Invalid Inputs");
+ return -1;
+ }
+
+ std::array<uint8_t, EVP_MAX_MD_SIZE> calMac;
+ size_t calMacLen = calMac.size();
+ // calculate MAC for the encrypted message.
+ if (NULL == HMAC(EVP_sha256(), key, keyLen, inBytes, inBytesLen,
+ calMac.data(),
+ reinterpret_cast<unsigned int*>(&calMacLen)))
+ {
+ log<level::DEBUG>("Error: Failed to calculate MAC");
+ return -1;
+ }
+ if (!((calMacLen == macLen) &&
+ (std::memcmp(calMac.data(), mac, calMacLen) == 0)))
+ {
+ log<level::DEBUG>("Authenticated message doesn't match");
+ return -1;
+ }
+
+ std::unique_ptr<EVP_CIPHER_CTX, decltype(&::EVP_CIPHER_CTX_free)> ctx(
+ EVP_CIPHER_CTX_new(), ::EVP_CIPHER_CTX_free);
+ EVP_CIPHER_CTX_set_padding(ctx.get(), 1);
+
+ // Set key & IV to decrypt
+ int retval = EVP_CipherInit_ex(ctx.get(), cipher, NULL, key, iv, 0);
+ if (!retval)
+ {
+ log<level::DEBUG>("EVP_CipherInit_ex failed",
+ entry("RET_VAL=%d", retval));
+ return -1;
+ }
+
+ int outLen = 0, outEVPLen = 0;
+ if ((retval = EVP_CipherUpdate(ctx.get(), outBytes + outLen, &outEVPLen,
+ inBytes, inBytesLen)))
+ {
+ outLen += outEVPLen;
+ if ((retval =
+ EVP_CipherFinal(ctx.get(), outBytes + outLen, &outEVPLen)))
+ {
+ outLen += outEVPLen;
+ *outBytesLen = outLen;
+ }
+ else
+ {
+ log<level::DEBUG>("EVP_CipherFinal fails",
+ entry("RET_VAL=%d", retval));
+ return -1;
+ }
+ }
+ else
+ {
+ log<level::DEBUG>("EVP_CipherUpdate fails",
+ entry("RET_VAL=%d", retval));
+ return -1;
+ }
+ return 0;
+}
+
+void PasswdMgr::initPasswordMap(void)
+{
+ phosphor::user::shadow::Lock lock();
+
+ std::array<uint8_t, maxKeySize> keyBuff;
+ std::ifstream keyFile(encryptKeyFileName, std::ios::in | std::ios::binary);
+ if (!keyFile.is_open())
+ {
+ log<level::DEBUG>("Error in opening encryption key file");
+ return;
+ }
+ keyFile.read((char*)keyBuff.data(), keyBuff.size());
+ if (keyFile.fail())
+ {
+ log<level::DEBUG>("Error in reading encryption key file");
+ return;
+ }
+
+ std::ifstream passwdFile(passwdFileName, std::ios::in | std::ios::binary);
+ if (!passwdFile.is_open())
+ {
+ log<level::DEBUG>("Error in opening ipmi password file");
+ return;
+ }
+
+ // calculate file size and read the data
+ std::vector<uint8_t> input;
+ passwdFile.seekg(0, std::ios::end);
+ ssize_t fileSize = passwdFile.tellg();
+ passwdFile.seekg(0, std::ios::beg);
+ input.resize(fileSize);
+ passwdFile.read((char*)input.data(), fileSize);
+ if (passwdFile.fail())
+ {
+ log<level::DEBUG>("Error in reading encryption key file");
+ return;
+ }
+
+ // verify the signature first
+ metaPassStruct* metaData = reinterpret_cast<metaPassStruct*>(input.data());
+ if (std::strncmp(metaData->signature, META_PASSWD_SIG,
+ sizeof(metaData->signature)))
+ {
+ log<level::DEBUG>("Error signature mismatch in password file");
+ return;
+ }
+
+ // compute the key needed to decrypt
+ std::array<uint8_t, EVP_MAX_KEY_LENGTH> key;
+ auto keyLen = key.size();
+ HMAC(EVP_sha256(), keyBuff.data(), keyBuff.size(),
+ input.data() + sizeof(*metaData), metaData->hashSize, key.data(),
+ reinterpret_cast<unsigned int*>(&keyLen));
+
+ // decrypt the data
+ uint8_t* iv = input.data() + sizeof(*metaData) + metaData->hashSize;
+ size_t ivLen = metaData->ivSize;
+ uint8_t* inBytes = iv + ivLen;
+ size_t inBytesLen = metaData->dataSize + metaData->padSize;
+ uint8_t* mac = inBytes + inBytesLen;
+ size_t macLen = metaData->macSize;
+ std::vector<uint8_t> outBytes(inBytesLen + EVP_MAX_BLOCK_LENGTH);
+ size_t outBytesLen = outBytes.size();
+ if (decrypt(EVP_aes_128_cbc(), key.data(), keyLen, iv, ivLen, inBytes,
+ inBytesLen, mac, macLen, outBytes.data(), &outBytesLen) != 0)
+ {
+ log<level::DEBUG>("Error in decryption");
+ return;
+ }
+ outBytes[outBytesLen] = 0;
+ OPENSSL_cleanse(key.data(), keyLen);
+ OPENSSL_cleanse(iv, ivLen);
+
+ // populate the user list with password
+ char* outPtr = reinterpret_cast<char*>(outBytes.data());
+ char* nToken = NULL;
+ char* linePtr = strtok_r(outPtr, "\n", &nToken);
+ size_t userEPos = 0, lineSize = 0;
+ while (linePtr != NULL)
+ {
+ std::string lineStr(linePtr);
+ if ((userEPos = lineStr.find(":")) != std::string::npos)
+ {
+ lineSize = lineStr.size();
+ passwdMapList.emplace(
+ lineStr.substr(0, userEPos),
+ lineStr.substr(userEPos + 1, lineSize - (userEPos + 1)));
+ }
+ linePtr = strtok_r(NULL, "\n", &nToken);
+ }
+ // Update the timestamp
+ struct stat fileStat = {};
+ if (stat(passwdFileName, &fileStat) != 0)
+ {
+ log<level::DEBUG>("Error in getting last updated time stamp");
+ return;
+ }
+ fileLastUpdatedTime = fileStat.st_mtime;
+ return;
+}
+
+} // namespace ipmi
diff --git a/user_channel/passwd_mgr.hpp b/user_channel/passwd_mgr.hpp
new file mode 100644
index 0000000..3078e21
--- /dev/null
+++ b/user_channel/passwd_mgr.hpp
@@ -0,0 +1,86 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#pragma once
+#include <openssl/evp.h>
+
+#include <ctime>
+#include <unordered_map>
+
+namespace ipmi
+{
+
+class PasswdMgr
+{
+ public:
+ ~PasswdMgr() = default;
+ PasswdMgr(const PasswdMgr&) = delete;
+ PasswdMgr& operator=(const PasswdMgr&) = delete;
+ PasswdMgr(PasswdMgr&&) = delete;
+ PasswdMgr& operator=(PasswdMgr&&) = delete;
+
+ /** @brief Constructs user password list
+ *
+ */
+ PasswdMgr();
+
+ /** @brief Get password for the user
+ *
+ * @param[in] userName - user name
+ *
+ * @return password string. will return empty string, if unable to locate
+ * the user
+ */
+ std::string getPasswdByUserName(const std::string& userName);
+
+ private:
+ using UserName = std::string;
+ using Password = std::string;
+ std::unordered_map<UserName, Password> passwdMapList;
+ std::time_t fileLastUpdatedTime;
+ /** @brief check timestamp and reload password map if required
+ *
+ */
+ void checkAndReload(void);
+ /** @brief initializes passwdMapList by reading the encrypted file
+ *
+ * Initializes the passwordMapList members after decrypting the
+ * password file. passwordMapList will be used further in IPMI
+ * authentication.
+ */
+ void initPasswordMap(void);
+ /** @brief decrypts the data provided
+ *
+ * @param[in] cipher - cipher to be used
+ * @param[in] key - pointer to the key
+ * @param[in] keyLen - Length of the key to be used
+ * @param[in] iv - pointer to initialization vector
+ * @param[in] ivLen - Length of the iv
+ * @param[in] inBytes - input data to be encrypted / decrypted
+ * @param[in] inBytesLen - input size to be decrypted
+ * @param[in] mac - message authentication code - to figure out corruption
+ * @param[in] macLen - size of MAC
+ * @param[in] outBytes - ptr to store output bytes
+ * @param[in] outBytesLen - outbut data length.
+ *
+ * @return error response
+ */
+ int decrypt(const EVP_CIPHER* cipher, uint8_t* key, size_t keyLen,
+ uint8_t* iv, size_t ivLen, uint8_t* inBytes, size_t inBytesLen,
+ uint8_t* mac, size_t macLen, uint8_t* outBytes,
+ size_t* outBytesLen);
+};
+
+} // namespace ipmi
diff --git a/user_channel/shadowlock.hpp b/user_channel/shadowlock.hpp
new file mode 100644
index 0000000..8b09f21
--- /dev/null
+++ b/user_channel/shadowlock.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include <shadow.h>
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+namespace phosphor
+{
+namespace user
+{
+namespace shadow
+{
+
+using InternalFailure =
+ sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+using namespace phosphor::logging;
+
+/** @class Lock
+ * @brief Responsible for locking and unlocking /etc/shadow
+ */
+class Lock
+{
+ public:
+ Lock(const Lock&) = delete;
+ Lock& operator=(const Lock&) = delete;
+ Lock(Lock&&) = delete;
+ Lock& operator=(Lock&&) = delete;
+
+ /** @brief Default constructor that just locks the shadow file */
+ Lock()
+ {
+ if (!lckpwdf())
+ {
+ log<level::ERR>("Locking Shadow failed");
+ elog<InternalFailure>();
+ }
+ }
+ ~Lock()
+ {
+ if (!ulckpwdf())
+ {
+ log<level::ERR>("Un-Locking Shadow failed");
+ elog<InternalFailure>();
+ }
+ }
+};
+
+} // namespace shadow
+} // namespace user
+} // namespace phosphor
diff --git a/user_channel/user_layer.cpp b/user_channel/user_layer.cpp
new file mode 100644
index 0000000..dce33d9
--- /dev/null
+++ b/user_channel/user_layer.cpp
@@ -0,0 +1,32 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include "user_layer.hpp"
+
+#include "passwd_mgr.hpp"
+namespace
+{
+ipmi::PasswdMgr passwdMgr;
+}
+
+namespace ipmi
+{
+std::string ipmiUserGetPassword(const std::string& userName)
+{
+ return passwdMgr.getPasswdByUserName(userName);
+}
+
+} // namespace ipmi
diff --git a/user_channel/user_layer.hpp b/user_channel/user_layer.hpp
new file mode 100644
index 0000000..b797007
--- /dev/null
+++ b/user_channel/user_layer.hpp
@@ -0,0 +1,33 @@
+/*
+// Copyright (c) 2018 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+#pragma once
+#include <host-ipmid/ipmid-api.h>
+
+#include <string>
+
+namespace ipmi
+{
+/** @brief The ipmi get user password layer call
+ *
+ * @param[in] userName
+ *
+ * @return password or empty string
+ */
+std::string ipmiUserGetPassword(const std::string& userName);
+
+// TODO: Define required user layer API Call's which user layer shared library
+// must implement.
+} // namespace ipmi
OpenPOWER on IntegriCloud