From b29b5ab3b9ad812949f3621a4422fde8d7c1c8d2 Mon Sep 17 00:00:00 2001 From: AppaRao Puli Date: Thu, 17 May 2018 10:28:48 +0530 Subject: Handling delete password entry from ipmi-pass API to Handle the delete password entry from ipmi-pass encrypted file when user gets deleted by any interface Change-Id: I692a81b166b53d6fc981fdb85ce5d6980887560b Signed-off-by: AppaRao Puli Signed-off-by: Richard Marian Thomaiyar --- user_channel/passwd_mgr.cpp | 446 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 376 insertions(+), 70 deletions(-) (limited to 'user_channel/passwd_mgr.cpp') diff --git a/user_channel/passwd_mgr.cpp b/user_channel/passwd_mgr.cpp index 1d8a1ce..15e6e34 100644 --- a/user_channel/passwd_mgr.cpp +++ b/user_channel/passwd_mgr.cpp @@ -16,15 +16,20 @@ #include "passwd_mgr.hpp" +#include "file.hpp" #include "shadowlock.hpp" #include +#include #include #include #include +#include +#include #include #include +#include #include namespace ipmi @@ -34,7 +39,12 @@ 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="; +#define META_PASSWD_SIG "=OPENBMC=" + +static inline size_t blockRound(size_t odd, size_t blk) +{ + return ((odd) + (((blk) - ((odd) & ((blk)-1))) & ((blk)-1))); +} /* * Meta data struct for encrypted password file @@ -68,16 +78,36 @@ std::string PasswdMgr::getPasswdByUserName(const std::string& userName) return iter->second; } -void PasswdMgr::checkAndReload(void) +int PasswdMgr::clearUserEntry(const std::string& userName) { - struct stat fileStat = {}; - if (stat(passwdFileName, &fileStat) != 0) + std::time_t updatedTime = getUpdatedFileTime(); + // Check file time stamp to know passwdMapList is up-to-date. + // If not up-to-date, then updatePasswdSpecialFile will read and + // check the user entry existance. + if (fileLastUpdatedTime == updatedTime && updatedTime != -EIO) { - log("Error in getting last updated time stamp"); - return; + if (passwdMapList.find(userName) == passwdMapList.end()) + { + log("User not found"); + return 0; + } } - std::time_t updatedTime = fileStat.st_mtime; - if (fileLastUpdatedTime != updatedTime) + + // Write passwdMap to Encryted file + if (updatePasswdSpecialFile(userName) != 0) + { + log("Passwd file update failed"); + return -EIO; + } + + log("Passwd file updated successfully"); + return 0; +} + +void PasswdMgr::checkAndReload(void) +{ + std::time_t updatedTime = getUpdatedFileTime(); + if (fileLastUpdatedTime != updatedTime && updatedTime != -1) { log("Reloading password map list"); passwdMapList.clear(); @@ -85,49 +115,55 @@ void PasswdMgr::checkAndReload(void) } } -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) +int PasswdMgr::encryptDecryptData(bool doEncrypt, 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, unsigned char* 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("Error Invalid Inputs"); - return -1; + return -EINVAL; } - std::array 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(&calMacLen))) + if (!doEncrypt) { - log("Error: Failed to calculate MAC"); - return -1; - } - if (!((calMacLen == macLen) && - (std::memcmp(calMac.data(), mac, calMacLen) == 0))) - { - log("Authenticated message doesn't match"); - return -1; + // verify MAC before decrypting the data. + std::array 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(&calMacLen))) + { + log("Error: Failed to calculate MAC"); + return -EIO; + } + if (!((calMacLen == *macLen) && + (std::memcmp(calMac.data(), mac, calMacLen) == 0))) + { + log("Authenticated message doesn't match"); + return -EBADMSG; + } } std::unique_ptr 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); + // Set key & IV + int retval = EVP_CipherInit_ex(ctx.get(), cipher, NULL, key, iv, + static_cast(doEncrypt)); if (!retval) { log("EVP_CipherInit_ex failed", entry("RET_VAL=%d", retval)); - return -1; + return -EIO; } int outLen = 0, outEVPLen = 0; @@ -145,14 +181,25 @@ int PasswdMgr::decrypt(const EVP_CIPHER* cipher, uint8_t* key, size_t keyLen, { log("EVP_CipherFinal fails", entry("RET_VAL=%d", retval)); - return -1; + return -EIO; } } else { log("EVP_CipherUpdate fails", entry("RET_VAL=%d", retval)); - return -1; + return -EIO; + } + + if (doEncrypt) + { + // Create MAC for the encrypted message + if (NULL == HMAC(EVP_sha256(), key, keyLen, outBytes, *outBytesLen, mac, + reinterpret_cast(macLen))) + { + log("Failed to create authentication"); + return -EIO; + } } return 0; } @@ -160,39 +207,73 @@ int PasswdMgr::decrypt(const EVP_CIPHER* cipher, uint8_t* key, size_t keyLen, void PasswdMgr::initPasswordMap(void) { phosphor::user::shadow::Lock lock(); + std::vector dataBuf; + if (readPasswdFileData(dataBuf) != 0) + { + log("Error in reading the encrypted pass file"); + return; + } + + if (dataBuf.size() != 0) + { + // populate the user list with password + char* outPtr = reinterpret_cast(dataBuf.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 + fileLastUpdatedTime = getUpdatedFileTime(); + return; +} + +int PasswdMgr::readPasswdFileData(std::vector& outBytes) +{ std::array keyBuff; std::ifstream keyFile(encryptKeyFileName, std::ios::in | std::ios::binary); if (!keyFile.is_open()) { log("Error in opening encryption key file"); - return; + return -EIO; } - keyFile.read((char*)keyBuff.data(), keyBuff.size()); + keyFile.read(reinterpret_cast(keyBuff.data()), keyBuff.size()); if (keyFile.fail()) { log("Error in reading encryption key file"); - return; + return -EIO; } std::ifstream passwdFile(passwdFileName, std::ios::in | std::ios::binary); if (!passwdFile.is_open()) { log("Error in opening ipmi password file"); - return; + return -EIO; } // calculate file size and read the data - std::vector 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); + std::vector input(fileSize); + passwdFile.read(reinterpret_cast(input.data()), fileSize); if (passwdFile.fail()) { log("Error in reading encryption key file"); - return; + return -EIO; } // verify the signature first @@ -201,61 +282,286 @@ void PasswdMgr::initPasswordMap(void) sizeof(metaData->signature))) { log("Error signature mismatch in password file"); - return; + return -EBADMSG; + } + + size_t inBytesLen = metaData->dataSize + metaData->padSize; + // If data is empty i.e no password map then return success + if (inBytesLen == 0) + { + log("Empty password file"); + return 0; } // compute the key needed to decrypt std::array key; auto keyLen = key.size(); - HMAC(EVP_sha256(), keyBuff.data(), keyBuff.size(), - input.data() + sizeof(*metaData), metaData->hashSize, key.data(), - reinterpret_cast(&keyLen)); + if (NULL == HMAC(EVP_sha256(), keyBuff.data(), keyBuff.size(), + input.data() + sizeof(*metaData), metaData->hashSize, + key.data(), reinterpret_cast(&keyLen))) + { + log("Failed to create MAC for authentication"); + return -EIO; + } // 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 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) + + size_t outBytesLen = 0; + // Resize to actual data size + outBytes.resize(inBytesLen + EVP_MAX_BLOCK_LENGTH); + if (encryptDecryptData(false, EVP_aes_128_cbc(), key.data(), keyLen, iv, + ivLen, inBytes, inBytesLen, mac, &macLen, + outBytes.data(), &outBytesLen) != 0) { log("Error in decryption"); - return; + return -EIO; } - outBytes[outBytesLen] = 0; + // Resize the vector to outBytesLen + outBytes.resize(outBytesLen); + OPENSSL_cleanse(key.data(), keyLen); OPENSSL_cleanse(iv, ivLen); - // populate the user list with password - char* outPtr = reinterpret_cast(outBytes.data()); - char* nToken = NULL; - char* linePtr = strtok_r(outPtr, "\n", &nToken); - size_t userEPos = 0, lineSize = 0; - while (linePtr != NULL) + return 0; +} + +int PasswdMgr::updatePasswdSpecialFile(const std::string& userName) +{ + phosphor::user::shadow::Lock lock(); + + size_t bytesWritten = 0; + size_t inBytesLen = 0; + size_t isUsrFound = false; + const EVP_CIPHER* cipher = EVP_aes_128_cbc(); + std::vector dataBuf; + + // Read the encrypted file and get the file data + // Check user existance and return if not exist. + if (readPasswdFileData(dataBuf) != 0) + { + log("Error in reading the encrypted pass file"); + return -EIO; + } + + if (dataBuf.size() != 0) + { + inBytesLen = dataBuf.size() + EVP_CIPHER_block_size(cipher); + } + + std::vector inBytes(inBytesLen); + if (inBytesLen != 0) { - std::string lineStr(linePtr); - if ((userEPos = lineStr.find(":")) != std::string::npos) + char* outPtr = reinterpret_cast(dataBuf.data()); + size_t userEPos = 0; + char* nToken = NULL; + char* linePtr = strtok_r(outPtr, "\n", &nToken); + while (linePtr != NULL) { - lineSize = lineStr.size(); - passwdMapList.emplace( - lineStr.substr(0, userEPos), - lineStr.substr(userEPos + 1, lineSize - (userEPos + 1))); + std::string lineStr(linePtr); + if ((userEPos = lineStr.find(":")) != std::string::npos) + { + if (userName.compare(lineStr.substr(0, userEPos)) == 0) + { + isUsrFound = true; + } + else + { + bytesWritten += std::snprintf( + reinterpret_cast(&inBytes[0]) + bytesWritten, + inBytesLen, "%s\n", lineStr.data()); + } + } + linePtr = strtok_r(NULL, "\n", &nToken); } - linePtr = strtok_r(NULL, "\n", &nToken); + + // Round of to block size and padding remaing bytes with zero. + inBytesLen = blockRound(bytesWritten, EVP_CIPHER_block_size(cipher)); + std::memset(&inBytes[0] + bytesWritten, 0, inBytesLen - bytesWritten); } - // Update the timestamp + if (!isUsrFound) + { + log("User doesn't exist"); + return 0; + } + + // Read the key buff from key file + std::array keyBuff; + std::ifstream keyFile(encryptKeyFileName, std::ios::in | std::ios::binary); + if (!keyFile.good()) + { + log("Error in opening encryption key file"); + return -EIO; + } + keyFile.read(reinterpret_cast(keyBuff.data()), keyBuff.size()); + if (keyFile.fail()) + { + log("Error in reading encryption key file"); + return -EIO; + } + keyFile.close(); + + // Read the original passwd file mode + struct stat st = {}; + if (stat(passwdFileName, &st) != 0) + { + log("Error in getting password file fstat()"); + return -EIO; + } + + // Create temporary file for write + std::string pwdFile(passwdFileName); + std::vector tempFileName(pwdFile.begin(), pwdFile.end()); + std::vector fileTemplate = {'_', '_', 'X', 'X', 'X', + 'X', 'X', 'X', '\0'}; + tempFileName.insert(tempFileName.end(), fileTemplate.begin(), + fileTemplate.end()); + int fd = mkstemp((char*)tempFileName.data()); + if (fd == -1) + { + log("Error creating temp file"); + return -EIO; + } + + std::string strTempFileName(tempFileName.data()); + // Open the temp file for writing from provided fd + // By "true", remove it at exit if still there. + // This is needed to cleanup the temp file at exception + phosphor::user::File temp(fd, strTempFileName, "w", true); + if ((temp)() == NULL) + { + close(fd); + log("Error creating temp file"); + return -EIO; + } + fd = -1; // don't use fd anymore, as the File object owns it + + // Set the file mode as of actual ipmi-pass file. + if (fchmod(fileno((temp)()), st.st_mode) < 0) + { + log("Error setting fchmod for temp file"); + return -EIO; + } + + const EVP_MD* digest = EVP_sha256(); + size_t hashLen = EVP_MD_block_size(digest); + std::vector hash(hashLen); + size_t ivLen = EVP_CIPHER_iv_length(cipher); + std::vector iv(ivLen); + std::array key; + size_t keyLen = key.size(); + std::array mac; + size_t macLen = mac.size(); + + // Create random hash and generate hash key which will be used for + // encryption. + if (RAND_bytes(hash.data(), hashLen) != 1) + { + log("Hash genertion failed, bailing out"); + return -EIO; + } + if (NULL == HMAC(digest, keyBuff.data(), keyBuff.size(), hash.data(), + hashLen, key.data(), + reinterpret_cast(&keyLen))) + { + log("Failed to create MAC for authentication"); + return -EIO; + } + + // Generate IV values + if (RAND_bytes(iv.data(), ivLen) != 1) + { + log("UV genertion failed, bailing out"); + return -EIO; + } + + // Encrypt the input data + std::vector outBytes(inBytesLen + EVP_MAX_BLOCK_LENGTH); + size_t outBytesLen = 0; + if (inBytesLen != 0) + { + if (encryptDecryptData(true, EVP_aes_128_cbc(), key.data(), keyLen, + iv.data(), ivLen, inBytes.data(), inBytesLen, + mac.data(), &macLen, outBytes.data(), + &outBytesLen) != 0) + { + log("Error while encrypting the data"); + return -EIO; + } + outBytes[outBytesLen] = 0; + } + OPENSSL_cleanse(key.data(), keyLen); + + // Update the meta password structure. + metaPassStruct metaData = {META_PASSWD_SIG, {0, 0}, 0, 0, 0, 0, 0}; + metaData.hashSize = hashLen; + metaData.ivSize = ivLen; + metaData.dataSize = bytesWritten; + metaData.padSize = outBytesLen - bytesWritten; + metaData.macSize = macLen; + + if (fwrite(&metaData, 1, sizeof(metaData), (temp)()) != sizeof(metaData)) + { + log("Error in writing meta data"); + return -EIO; + } + + if (fwrite(&hash[0], 1, hashLen, (temp)()) != hashLen) + { + log("Error in writing hash data"); + return -EIO; + } + + if (fwrite(&iv[0], 1, ivLen, (temp)()) != ivLen) + { + log("Error in writing IV data"); + return -EIO; + } + + if (fwrite(&outBytes[0], 1, outBytesLen, (temp)()) != outBytesLen) + { + log("Error in writing encrypted data"); + return -EIO; + } + + if (fwrite(&mac[0], 1, macLen, (temp)()) != macLen) + { + log("Error in writing MAC data"); + return -EIO; + } + + if (fflush((temp)())) + { + log( + "File fflush error while writing entries to special file"); + return -EIO; + } + + OPENSSL_cleanse(iv.data(), ivLen); + + // Rename the tmp file to actual file + if (std::rename(strTempFileName.data(), passwdFileName) != 0) + { + log("Failed to rename tmp file to ipmi-pass"); + return -EIO; + } + + return 0; +} + +std::time_t PasswdMgr::getUpdatedFileTime() +{ struct stat fileStat = {}; if (stat(passwdFileName, &fileStat) != 0) { - log("Error in getting last updated time stamp"); - return; + log("Error - Getting passwd file time stamp"); + return -EIO; } - fileLastUpdatedTime = fileStat.st_mtime; - return; + return fileStat.st_mtime; } } // namespace ipmi -- cgit v1.2.1