diff options
Diffstat (limited to 'user_channel/passwd_mgr.cpp')
-rw-r--r-- | user_channel/passwd_mgr.cpp | 261 |
1 files changed, 261 insertions, 0 deletions
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 |